/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome

« back to all changes in this revision

Viewing changes to lib/stdhome/walker/copy_base.py

  • Committer: Tim Marston
  • Date: 2014-04-05 22:27:09 UTC
  • Revision ID: tim@ed.am-20140405222709-4mp50aiu184blnf1
fixed file_matcher

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
 
22
22
import filecmp, os, shutil
23
 
from .walker import Walker
24
 
from stdhome import the
25
 
from stdhome import util
 
23
from walker import Walker
 
24
from stdhome.file_matcher import FileMatcher
 
25
import stdhome.the as the
26
26
 
27
27
 
28
28
class CopyBaseWalker( Walker ):
29
29
        """The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
30
        It is designed to be the base class of both the copy-in and copy-out walker,
31
31
        both of which are specialisations of this purpose.  See them for more
32
 
        information.  The report method, which can be overridden in derived classes,
33
 
        takes, in addition to the relative filename, a source file type, an
34
 
        operation, and a destination file type.  Valid file types are f (file), l
35
 
        (symlink), d (directory) and _ (non-existant).  Valid operations are *
36
 
        (modify), = (skip: same), @ (skip: symlink substitute) and # (skip:
37
 
        ignored).
38
 
 
 
32
        information.
39
33
        """
40
34
 
41
 
 
42
 
        def __init__( self ):
43
 
                self.check_src_symlinks = False
44
 
                self.check_dst_symlinks = False
45
 
                self.check_dst_ignores = False
46
 
                self.report = False
47
 
 
48
 
 
49
 
        def print_op( self, rel_file, src, op, dst ):
50
 
 
51
 
                # report changes
52
 
                if self.report and op == '*':
53
 
                        if dst == '_':
54
 
                                print(" N  %s" % ( rel_file ))
55
 
                        elif src == '_':
56
 
                                print(" D  %s" % ( rel_file ))
57
 
                        elif src == dst:
58
 
                                print(" M  %s" % ( rel_file ))
59
 
                        else:
60
 
                                print(" K  %s" % ( rel_file ))
 
35
        def __init__( self, updated_files = None ):
 
36
                self.accept_list = FileMatcher()
61
37
 
62
38
 
63
39
        def process( self, rel_file, src, dst ):
64
40
 
65
 
                # ignore?
66
 
                if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
67
 
                        self.print_op( rel_file, src.type, '#', dst.type )
68
 
                        return True
69
 
 
70
41
                # src entity is directory
71
42
                if src.type == 'd':
72
43
 
73
 
                        # if dst entity doesn't exist, create it (and recurse)
 
44
                        # if dst entity doesn't exist, create directory (no need to recurse,
 
45
                        # since we're copying the whole directory)
74
46
                        if dst.type == '_':
75
 
                                self.print_op( rel_file, 'd', '*', '_' )
 
47
                                if the.verbose > 1: self.print_op( rel_file, 'd>_' )
76
48
                                os.mkdir( dst.file )
77
49
                                shutil.copystat( src.file, dst.file )
78
 
                                return True
 
50
                                return False
79
51
 
80
52
                        # if dst entity is a directory, copy permissions, as required (and
81
53
                        # recurse)
82
54
                        elif dst.type == 'd':
83
 
                                if os.stat( src.file ).st_mode != os.stat( dst.file ).st_mode:
84
 
                                        self.print_op( rel_file, 'd', '*', 'd' )
85
 
                                        shutil.copystat( src.file, dst.file )
86
 
                                else:
87
 
                                        self.print_op( rel_file, 'd', '=', 'd' )
 
55
                                # TODO: should check permission and only do as necessary
 
56
                                if the.verbose > 1: self.print_op( rel_file, 'd>d' )
 
57
                                shutil.copystat( src.file, dst.file )
88
58
                                return True
89
59
 
90
 
                        # if dst entity is a symlink to a directory, and this is an
91
 
                        # acceptable substitute, just recurse
92
 
                        elif self.check_dst_symlinks and dst.link_type == 'd' and \
93
 
                                        the.config.symlinks.matches( rel_file ):
94
 
                                self.print_op( rel_file, 'd', '@', 'd' )
 
60
                        # TODO: if dst entity is a symlink to a directory, and rel_file is
 
61
                        # matched by the acceptable symlinks file matcher, just recurse
 
62
                        elif dst.link_type == 'd' and self.accept_list.match( rel_file ):
 
63
                                if the.verbose > 1: self.print_op( rel_file, 'd@d' )
95
64
                                return True
96
65
 
97
66
                        # if dst entity is a file or symlink in home dir, replace it with
98
67
                        # directory (no need to recurse, since we're copying the whole
99
68
                        # directory)
100
69
                        elif dst.type == 'f' or dst.type == 'l':
101
 
                                self.print_op( rel_file, 'd', '*', dst.type )
 
70
                                if the.verbose > 1: self.print_op( rel_file, 'd>' + dst.type )
102
71
                                os.unlink( dst.file )
103
 
                                shutil.copytree( src.file, dst.file, True )
 
72
                                os.mkdir( dst.file )
 
73
                                shutil.copystat( src.file, dst.file )
 
74
                                return False
104
75
 
105
76
                        else:
106
77
                                raise NotImplementedError()
107
78
 
108
79
                # src entity is file
109
 
                elif src.type == 'f':
 
80
                if src.type == 'f':
110
81
 
111
82
                        # if dst entity doesn't exist, copy file
112
83
                        if dst.type == '_':
113
 
                                self.print_op( rel_file, 'f', '*', '_' )
 
84
                                if the.verbose > 1: self.print_op( rel_file, 'f>_' )
114
85
                                shutil.copy( src.file, dst.file )
115
86
                                shutil.copystat( src.file, dst.file )
116
87
 
117
88
                        # if dst entity is a file, replace it only if it differs
118
89
                        elif dst.type == 'f':
119
90
                                if not filecmp.cmp( src.file, dst.file ):
120
 
                                        self.print_op( rel_file, 'f', '*', 'f' )
 
91
                                        if the.verbose > 1: self.print_op( rel_file, 'f>f' )
121
92
                                        os.unlink( dst.file )
122
93
                                        shutil.copy( src.file, dst.file )
123
94
                                        shutil.copystat( src.file, dst.file )
124
95
                                else:
125
 
                                        self.print_op( rel_file, 'f', '=', 'f' )
 
96
                                        if the.verbose > 1: self.print_op( rel_file, 'f=f' )
126
97
 
127
98
                        # if dst entity is a directory, replace it with file
128
99
                        elif dst.type == 'd':
129
 
                                self.print_op( rel_file, 'f', '*', 'd' )
 
100
                                if the.verbose > 1: self.print_op( rel_file, 'f>d' )
130
101
                                shutil.rmtree( dst.file )
131
102
                                shutil.copy( src.file, dst.file )
132
103
                                shutil.copystat( src.file, dst.file )
133
104
 
134
105
                        # if dst entity is a symlink, replace it with file
135
106
                        elif dst.type == 'l':
136
 
                                self.print_op( rel_file, 'f', '*', 'l' )
 
107
                                if the.verbose > 1: self.print_op( rel_file, 'f>l' )
137
108
                                os.unlink( dst.file )
138
109
                                shutil.copy( src.file, dst.file )
139
110
                                shutil.copystat( src.file, dst.file )
142
113
                                raise NotImplementedError()
143
114
 
144
115
                # src entity is a symlink
145
 
                elif src.type == 'l':
 
116
                if src.type == 'l':
146
117
 
147
118
                        # if dst entity doesn't exist, copy symlink
148
119
                        if dst.type == '_':
149
 
                                self.print_op( rel_file, 'l', '*', '_' )
 
120
                                if the.verbose > 1: self.print_op( rel_file, 'l>_' )
150
121
                                os.symlink( os.readlink( src.file ), dst.file )
151
122
 
152
123
                        # if dst entity is a symlink, replace it only if it differs
153
124
                        elif dst.type == 'l':
154
125
                                if os.readlink( src.file ) != os.readlink( dst.file ):
155
 
                                        self.print_op( rel_file, 'l', '*', 'l' )
 
126
                                        if the.verbose > 1: self.print_op( rel_file, 'l>l' )
156
127
                                        os.unlink( dst.file )
157
128
                                        os.symlink( os.readlink( src.file ), dst.file )
158
129
                                else:
159
 
                                        self.print_op( rel_file, 'l', '=', 'l' )
 
130
                                        if the.verbose > 1: self.print_op( rel_file, 'l=l' )
160
131
 
161
132
                        # if dst entity is a file, replace it with symlink
162
133
                        elif dst.type == 'f':
163
 
                                self.print_op( rel_file, 'l', '*', 'f' )
 
134
                                if the.verbose > 1: self.print_op( rel_file, 'l>f' )
164
135
                                os.unlink( dst.file )
165
136
                                os.symlink( os.readlink( src.file ), dst.file )
166
137
 
167
 
                        # if dst entity is a directory...
 
138
                        # if dst entity is a directory, replace it with symlink
168
139
                        elif dst.type == 'd':
169
 
 
170
 
                                # if src entity is a symlink to a directory, and this is an
171
 
                                # acceptable substitute, just recurse
172
 
                                if self.check_src_symlinks and src.link_type == 'd' and \
173
 
                                                the.config.symlinks.matches( rel_file ):
174
 
                                        self.print_op( rel_file, 'd', '@', 'd' )
175
 
                                        return True
176
 
 
177
 
                                # else replace it with a symlink
178
 
                                self.print_op( rel_file, 'l', '*', 'd' )
 
140
                                if the.verbose > 1: self.print_op( rel_file, 'l>d' )
179
141
                                shutil.rmtree( dst.file )
180
142
                                os.symlink( os.readlink( src.file ), dst.file )
181
143
 
183
145
                                raise NotImplementedError()
184
146
 
185
147
                # src entity is missing
186
 
                elif src.type == '_':
 
148
                if src.type == '_':
187
149
 
188
150
                        # if dst entity doesn't exist, we're good
189
151
                        if dst.type == '_':
191
153
 
192
154
                        # if dst entity is a file or symlink, delete it
193
155
                        elif dst.type == 'f' or dst.type == 'l':
194
 
                                self.print_op( rel_file, '_', '*', dst.type )
 
156
                                if the.verbose > 1: self.print_op( rel_file, '_>' + dst.type )
195
157
                                os.unlink( dst.file )
196
158
 
197
159
                        # if dst entity is a directory, delete it
198
160
                        elif dst.type == 'd':
199
 
                                self.print_op( rel_file, '_', '*', 'd' )
 
161
                                if the.verbose > 1: self.print_op( rel_file, '_>d' )
200
162
                                shutil.rmtree( dst.file )
201
163
 
202
164
                        else:
203
165
                                raise NotImplementedError()
204
166
 
205
 
                # if we got here, we don't want to recurse...
 
167
                # non-directories can not be recursed in to
206
168
                return False