/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-09-01 12:28:59 UTC
  • Revision ID: tim@ed.am-20210901122859-tkvlmry7hef8ahwc
moved bzr -> brz

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
41
 
36
43
                self.check_src_symlinks = False
37
44
                self.check_dst_symlinks = False
38
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 ))
39
61
 
40
62
 
41
63
        def process( self, rel_file, src, dst ):
42
64
 
43
65
                # ignore?
44
66
                if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
45
 
                        if the.verbose >= 2:
46
 
                                self.print_op( rel_file, '%s#%s' % ( src.type, dst.type ) )
 
67
                        self.print_op( rel_file, src.type, '#', dst.type )
47
68
                        return True
48
69
 
49
70
                # src entity is directory
50
71
                if src.type == 'd':
51
72
 
52
 
                        # if dst entity doesn't exist, create directory (no need to recurse,
53
 
                        # since we're copying the whole directory)
 
73
                        # if dst entity doesn't exist, create it (and recurse)
54
74
                        if dst.type == '_':
55
 
                                if the.verbose >= 2: self.print_op( rel_file, 'd>_' )
56
 
                                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
57
79
 
58
80
                        # if dst entity is a directory, copy permissions, as required (and
59
81
                        # recurse)
60
82
                        elif dst.type == 'd':
61
 
                                # TODO: should check permission and only do as necessary
62
 
                                if the.verbose >= 2: self.print_op( rel_file, 'd>d' )
63
 
                                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' )
64
88
                                return True
65
89
 
66
90
                        # if dst entity is a symlink to a directory, and this is an
67
91
                        # acceptable substitute, just recurse
68
92
                        elif self.check_dst_symlinks and dst.link_type == 'd' and \
69
93
                                        the.config.symlinks.matches( rel_file ):
70
 
                                if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
 
94
                                self.print_op( rel_file, 'd', '@', 'd' )
71
95
                                return True
72
96
 
73
97
                        # if dst entity is a file or symlink in home dir, replace it with
74
98
                        # directory (no need to recurse, since we're copying the whole
75
99
                        # directory)
76
100
                        elif dst.type == 'f' or dst.type == 'l':
77
 
                                if the.verbose >= 2: self.print_op( rel_file, 'd>' + dst.type )
 
101
                                self.print_op( rel_file, 'd', '*', dst.type )
78
102
                                os.unlink( dst.file )
79
103
                                shutil.copytree( src.file, dst.file, True )
80
104
 
86
110
 
87
111
                        # if dst entity doesn't exist, copy file
88
112
                        if dst.type == '_':
89
 
                                if the.verbose >= 2: self.print_op( rel_file, 'f>_' )
 
113
                                self.print_op( rel_file, 'f', '*', '_' )
90
114
                                shutil.copy( src.file, dst.file )
91
115
                                shutil.copystat( src.file, dst.file )
92
116
 
93
117
                        # if dst entity is a file, replace it only if it differs
94
118
                        elif dst.type == 'f':
95
119
                                if not filecmp.cmp( src.file, dst.file ):
96
 
                                        if the.verbose >= 2: self.print_op( rel_file, 'f>f' )
 
120
                                        self.print_op( rel_file, 'f', '*', 'f' )
97
121
                                        os.unlink( dst.file )
98
122
                                        shutil.copy( src.file, dst.file )
99
123
                                        shutil.copystat( src.file, dst.file )
100
124
                                else:
101
 
                                        if the.verbose >= 2: self.print_op( rel_file, 'f=f' )
 
125
                                        self.print_op( rel_file, 'f', '=', 'f' )
102
126
 
103
127
                        # if dst entity is a directory, replace it with file
104
128
                        elif dst.type == 'd':
105
 
                                if the.verbose >= 2: self.print_op( rel_file, 'f>d' )
 
129
                                self.print_op( rel_file, 'f', '*', 'd' )
106
130
                                shutil.rmtree( dst.file )
107
131
                                shutil.copy( src.file, dst.file )
108
132
                                shutil.copystat( src.file, dst.file )
109
133
 
110
134
                        # if dst entity is a symlink, replace it with file
111
135
                        elif dst.type == 'l':
112
 
                                if the.verbose >= 2: self.print_op( rel_file, 'f>l' )
 
136
                                self.print_op( rel_file, 'f', '*', 'l' )
113
137
                                os.unlink( dst.file )
114
138
                                shutil.copy( src.file, dst.file )
115
139
                                shutil.copystat( src.file, dst.file )
122
146
 
123
147
                        # if dst entity doesn't exist, copy symlink
124
148
                        if dst.type == '_':
125
 
                                if the.verbose >= 2: self.print_op( rel_file, 'l>_' )
 
149
                                self.print_op( rel_file, 'l', '*', '_' )
126
150
                                os.symlink( os.readlink( src.file ), dst.file )
127
151
 
128
152
                        # if dst entity is a symlink, replace it only if it differs
129
153
                        elif dst.type == 'l':
130
154
                                if os.readlink( src.file ) != os.readlink( dst.file ):
131
 
                                        if the.verbose >= 2: self.print_op( rel_file, 'l>l' )
 
155
                                        self.print_op( rel_file, 'l', '*', 'l' )
132
156
                                        os.unlink( dst.file )
133
157
                                        os.symlink( os.readlink( src.file ), dst.file )
134
158
                                else:
135
 
                                        if the.verbose >= 2: self.print_op( rel_file, 'l=l' )
 
159
                                        self.print_op( rel_file, 'l', '=', 'l' )
136
160
 
137
161
                        # if dst entity is a file, replace it with symlink
138
162
                        elif dst.type == 'f':
139
 
                                if the.verbose >= 2: self.print_op( rel_file, 'l>f' )
 
163
                                self.print_op( rel_file, 'l', '*', 'f' )
140
164
                                os.unlink( dst.file )
141
165
                                os.symlink( os.readlink( src.file ), dst.file )
142
166
 
147
171
                                # acceptable substitute, just recurse
148
172
                                if self.check_src_symlinks and src.link_type == 'd' and \
149
173
                                                the.config.symlinks.matches( rel_file ):
150
 
                                        if the.verbose >= 2: self.print_op( rel_file, 'd@d' )
 
174
                                        self.print_op( rel_file, 'd', '@', 'd' )
151
175
                                        return True
152
176
 
153
177
                                # else replace it with a symlink
154
 
                                if the.verbose >= 2: self.print_op( rel_file, 'l>d' )
 
178
                                self.print_op( rel_file, 'l', '*', 'd' )
155
179
                                shutil.rmtree( dst.file )
156
180
                                os.symlink( os.readlink( src.file ), dst.file )
157
181
 
167
191
 
168
192
                        # if dst entity is a file or symlink, delete it
169
193
                        elif dst.type == 'f' or dst.type == 'l':
170
 
                                if the.verbose >= 2: self.print_op( rel_file, '_>' + dst.type )
 
194
                                self.print_op( rel_file, '_', '*', dst.type )
171
195
                                os.unlink( dst.file )
172
196
 
173
197
                        # if dst entity is a directory, delete it
174
198
                        elif dst.type == 'd':
175
 
                                if the.verbose >= 2: self.print_op( rel_file, '_>d' )
 
199
                                self.print_op( rel_file, '_', '*', 'd' )
176
200
                                shutil.rmtree( dst.file )
177
201
 
178
202
                        else: