/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-18 14:42:00 UTC
  • Revision ID: tim@ed.am-20140418144200-ksbr3l58p2v3jhy2
fixed bugs in command.expand_files, where results could be not unique, nor
sorted, and make sure the last component of the files is not resolved if it is a
symlink

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
import stdhome.the as the
26
25
 
27
26
 
28
27
class CopyBaseWalker( Walker ):
29
28
        """The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
29
        It is designed to be the base class of both the copy-in and copy-out walker,
31
30
        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
 
 
 
31
        information.
39
32
        """
40
33
 
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 ))
 
34
        accept_list = None
61
35
 
62
36
 
63
37
        def process( self, rel_file, src, dst ):
64
38
 
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
39
                # src entity is directory
71
40
                if src.type == 'd':
72
41
 
73
 
                        # if dst entity doesn't exist, create it (and recurse)
 
42
                        # if dst entity doesn't exist, create directory (no need to recurse,
 
43
                        # since we're copying the whole directory)
74
44
                        if dst.type == '_':
75
 
                                self.print_op( rel_file, 'd', '*', '_' )
76
 
                                os.mkdir( dst.file )
77
 
                                shutil.copystat( src.file, dst.file )
78
 
                                return True
 
45
                                if the.verbose >= 2: self.print_op( rel_file, 'd>_' )
 
46
                                shutil.copytree( src.file, dst.file, True )
79
47
 
80
48
                        # if dst entity is a directory, copy permissions, as required (and
81
49
                        # recurse)
82
50
                        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' )
 
51
                                # TODO: should check permission and only do as necessary
 
52
                                if the.verbose >= 2: self.print_op( rel_file, 'd>d' )
 
53
                                shutil.copystat( src.file, dst.file )
88
54
                                return True
89
55
 
90
56
                        # if dst entity is a symlink to a directory, and this is an
91
57
                        # 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' )
 
58
                        elif dst.link_type == 'd' and \
 
59
                                        self.accept_list and self.accept_list.match( rel_file ):
 
60
                                if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
95
61
                                return True
96
62
 
97
63
                        # if dst entity is a file or symlink in home dir, replace it with
98
64
                        # directory (no need to recurse, since we're copying the whole
99
65
                        # directory)
100
66
                        elif dst.type == 'f' or dst.type == 'l':
101
 
                                self.print_op( rel_file, 'd', '*', dst.type )
 
67
                                if the.verbose >= 2: self.print_op( rel_file, 'd>' + dst.type )
102
68
                                os.unlink( dst.file )
103
69
                                shutil.copytree( src.file, dst.file, True )
104
70
 
110
76
 
111
77
                        # if dst entity doesn't exist, copy file
112
78
                        if dst.type == '_':
113
 
                                self.print_op( rel_file, 'f', '*', '_' )
 
79
                                if the.verbose >= 2: self.print_op( rel_file, 'f>_' )
114
80
                                shutil.copy( src.file, dst.file )
115
81
                                shutil.copystat( src.file, dst.file )
116
82
 
117
83
                        # if dst entity is a file, replace it only if it differs
118
84
                        elif dst.type == 'f':
119
85
                                if not filecmp.cmp( src.file, dst.file ):
120
 
                                        self.print_op( rel_file, 'f', '*', 'f' )
 
86
                                        if the.verbose >= 2: self.print_op( rel_file, 'f>f' )
121
87
                                        os.unlink( dst.file )
122
88
                                        shutil.copy( src.file, dst.file )
123
89
                                        shutil.copystat( src.file, dst.file )
124
90
                                else:
125
 
                                        self.print_op( rel_file, 'f', '=', 'f' )
 
91
                                        if the.verbose >= 2: self.print_op( rel_file, 'f=f' )
126
92
 
127
93
                        # if dst entity is a directory, replace it with file
128
94
                        elif dst.type == 'd':
129
 
                                self.print_op( rel_file, 'f', '*', 'd' )
 
95
                                if the.verbose >= 2: self.print_op( rel_file, 'f>d' )
130
96
                                shutil.rmtree( dst.file )
131
97
                                shutil.copy( src.file, dst.file )
132
98
                                shutil.copystat( src.file, dst.file )
133
99
 
134
100
                        # if dst entity is a symlink, replace it with file
135
101
                        elif dst.type == 'l':
136
 
                                self.print_op( rel_file, 'f', '*', 'l' )
 
102
                                if the.verbose >= 2: self.print_op( rel_file, 'f>l' )
137
103
                                os.unlink( dst.file )
138
104
                                shutil.copy( src.file, dst.file )
139
105
                                shutil.copystat( src.file, dst.file )
146
112
 
147
113
                        # if dst entity doesn't exist, copy symlink
148
114
                        if dst.type == '_':
149
 
                                self.print_op( rel_file, 'l', '*', '_' )
 
115
                                if the.verbose >= 2: self.print_op( rel_file, 'l>_' )
150
116
                                os.symlink( os.readlink( src.file ), dst.file )
151
117
 
152
118
                        # if dst entity is a symlink, replace it only if it differs
153
119
                        elif dst.type == 'l':
154
120
                                if os.readlink( src.file ) != os.readlink( dst.file ):
155
 
                                        self.print_op( rel_file, 'l', '*', 'l' )
 
121
                                        if the.verbose >= 2: self.print_op( rel_file, 'l>l' )
156
122
                                        os.unlink( dst.file )
157
123
                                        os.symlink( os.readlink( src.file ), dst.file )
158
124
                                else:
159
 
                                        self.print_op( rel_file, 'l', '=', 'l' )
 
125
                                        if the.verbose >= 2: self.print_op( rel_file, 'l=l' )
160
126
 
161
127
                        # if dst entity is a file, replace it with symlink
162
128
                        elif dst.type == 'f':
163
 
                                self.print_op( rel_file, 'l', '*', 'f' )
 
129
                                if the.verbose >= 2: self.print_op( rel_file, 'l>f' )
164
130
                                os.unlink( dst.file )
165
131
                                os.symlink( os.readlink( src.file ), dst.file )
166
132
 
167
 
                        # if dst entity is a directory...
 
133
                        # if dst entity is a directory, and src entity is a symlink to a
 
134
                        # directory, and this is an acceptable substitute, just recurse
 
135
                        elif dst.type == 'd' and src.link_type == 'd' and \
 
136
                                        self.accept_list and self.accept_list.match( rel_file ):
 
137
                                if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
 
138
                                return True
 
139
 
 
140
                        # if dst entity is a directory, replace it with symlink
168
141
                        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' )
 
142
                                if the.verbose >= 2: self.print_op( rel_file, 'l>d' )
179
143
                                shutil.rmtree( dst.file )
180
144
                                os.symlink( os.readlink( src.file ), dst.file )
181
145
 
191
155
 
192
156
                        # if dst entity is a file or symlink, delete it
193
157
                        elif dst.type == 'f' or dst.type == 'l':
194
 
                                self.print_op( rel_file, '_', '*', dst.type )
 
158
                                if the.verbose >= 2: self.print_op( rel_file, '_>' + dst.type )
195
159
                                os.unlink( dst.file )
196
160
 
197
161
                        # if dst entity is a directory, delete it
198
162
                        elif dst.type == 'd':
199
 
                                self.print_op( rel_file, '_', '*', 'd' )
 
163
                                if the.verbose >= 2: self.print_op( rel_file, '_>d' )
200
164
                                shutil.rmtree( dst.file )
201
165
 
202
166
                        else: