/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: 2021-07-05 19:14:32 UTC
  • Revision ID: tim@ed.am-20210705191432-243ayb7s2nmussvi
python3ification

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