/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
1
# copy_base.py
2
#
3
# Copyright (C) 2013 to 2014 Tim Marston <tim@edm.am>
4
#
5
# This file is part of stdhome (hereafter referred to as "this program").
6
# See http://ed.am/dev/stdhome for more information.
7
#
8
# This program is free software: you can redistribute it and/or modify
9
# it under the terms of the GNU General Public License as published by
10
# the Free Software Foundation, either version 3 of the License, or
11
# (at your option) any later version.
12
#
13
# This program is distributed in the hope that it will be useful,
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
# GNU General Public License for more details.
17
#
18
# You should have received a copy of the GNU General Public License
19
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
21
22
import filecmp, os, shutil
88 by Tim Marston
python3ification
23
from .walker import Walker
66 by Tim Marston
check directories for (permission) changes in copy base walker
24
from stdhome import the
25
from stdhome import util
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
26
27
28
class CopyBaseWalker( Walker ):
29
	"""The copy-base walker traverses a walklist ruthlessly mirroring src to dst.
30
	It is designed to be the base class of both the copy-in and copy-out walker,
31
	both of which are specialisations of this purpose.  See them for more
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
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
85 by Tim Marston
typo in comment
34
	operation, and a destination file type.  Valid file types are f (file), l
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
35
	(symlink), d (directory) and _ (non-existant).  Valid operations are *
36
	(modify), = (skip: same), @ (skip: symlink substitute) and # (skip:
37
	ignored).
38
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
39
	"""
40
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
41
42
	def __init__( self ):
43
		self.check_src_symlinks = False
44
		self.check_dst_symlinks = False
45
		self.check_dst_ignores = False
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
46
		self.report = False
47
48
49
	def print_op( self, rel_file, src, op, dst ):
50
51
		# report changes
84 by Tim Marston
changed copy base change reporting so that it doesn't silence when verbose
52
		if self.report and op == '*':
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
53
			if dst == '_':
88 by Tim Marston
python3ification
54
				print(" N  %s" % ( rel_file ))
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
55
			elif src == '_':
88 by Tim Marston
python3ification
56
				print(" D  %s" % ( rel_file ))
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
57
			elif src == dst:
88 by Tim Marston
python3ification
58
				print(" M  %s" % ( rel_file ))
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
59
			else:
88 by Tim Marston
python3ification
60
				print(" K  %s" % ( rel_file ))
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
61
62
63
	def process( self, rel_file, src, dst ):
64
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
65
		# ignore?
66
		if self.check_dst_ignores and the.config.ignores.matches( rel_file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
67
			self.print_op( rel_file, src.type, '#', dst.type )
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
68
			return True
69
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
70
		# src entity is directory
71
		if src.type == 'd':
72
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
73
			# if dst entity doesn't exist, create it (and recurse)
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
74
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
75
				self.print_op( rel_file, 'd', '*', '_' )
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
76
				os.mkdir( dst.file )
77
				shutil.copystat( src.file, dst.file )
78
				return True
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
79
80
			# if dst entity is a directory, copy permissions, as required (and
81
			# recurse)
82
			elif dst.type == 'd':
66 by Tim Marston
check directories for (permission) changes in copy base walker
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' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
88
				return True
89
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
90
			# if dst entity is a symlink to a directory, and this is an
91
			# acceptable substitute, just recurse
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
92
			elif self.check_dst_symlinks and dst.link_type == 'd' and \
93
					the.config.symlinks.matches( rel_file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
94
				self.print_op( rel_file, 'd', '@', 'd' )
23 by Tim Marston
implemented CopyInWalker in terms of CopyBaseWalker, changed implementation of
95
				return True
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
96
97
			# if dst entity is a file or symlink in home dir, replace it with
98
			# directory (no need to recurse, since we're copying the whole
99
			# directory)
100
			elif dst.type == 'f' or dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
101
				self.print_op( rel_file, 'd', '*', dst.type )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
102
				os.unlink( dst.file )
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
103
				shutil.copytree( src.file, dst.file, True )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
104
105
			else:
106
				raise NotImplementedError()
107
108
		# src entity is file
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
109
		elif src.type == 'f':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
110
111
			# if dst entity doesn't exist, copy file
112
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
113
				self.print_op( rel_file, 'f', '*', '_' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
114
				shutil.copy( src.file, dst.file )
115
				shutil.copystat( src.file, dst.file )
116
117
			# if dst entity is a file, replace it only if it differs
118
			elif dst.type == 'f':
119
				if not filecmp.cmp( src.file, dst.file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
120
					self.print_op( rel_file, 'f', '*', 'f' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
121
					os.unlink( dst.file )
122
					shutil.copy( src.file, dst.file )
123
					shutil.copystat( src.file, dst.file )
124
				else:
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
125
					self.print_op( rel_file, 'f', '=', 'f' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
126
127
			# if dst entity is a directory, replace it with file
128
			elif dst.type == 'd':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
129
				self.print_op( rel_file, 'f', '*', 'd' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
130
				shutil.rmtree( dst.file )
131
				shutil.copy( src.file, dst.file )
132
				shutil.copystat( src.file, dst.file )
133
134
			# if dst entity is a symlink, replace it with file
135
			elif dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
136
				self.print_op( rel_file, 'f', '*', 'l' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
137
				os.unlink( dst.file )
138
				shutil.copy( src.file, dst.file )
139
				shutil.copystat( src.file, dst.file )
140
141
			else:
142
				raise NotImplementedError()
143
144
		# src entity is a symlink
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
145
		elif src.type == 'l':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
146
147
			# if dst entity doesn't exist, copy symlink
148
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
149
				self.print_op( rel_file, 'l', '*', '_' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
150
				os.symlink( os.readlink( src.file ), dst.file )
151
152
			# if dst entity is a symlink, replace it only if it differs
153
			elif dst.type == 'l':
154
				if os.readlink( src.file ) != os.readlink( dst.file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
155
					self.print_op( rel_file, 'l', '*', 'l' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
156
					os.unlink( dst.file )
157
					os.symlink( os.readlink( src.file ), dst.file )
158
				else:
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
159
					self.print_op( rel_file, 'l', '=', 'l' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
160
161
			# if dst entity is a file, replace it with symlink
162
			elif dst.type == 'f':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
163
				self.print_op( rel_file, 'l', '*', 'f' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
164
				os.unlink( dst.file )
165
				os.symlink( os.readlink( src.file ), dst.file )
166
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
167
			# if dst entity is a directory...
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
168
			elif dst.type == 'd':
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
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 ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
174
					self.print_op( rel_file, 'd', '@', 'd' )
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
175
					return True
176
177
				# else replace it with a symlink
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
178
				self.print_op( rel_file, 'l', '*', 'd' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
179
				shutil.rmtree( dst.file )
180
				os.symlink( os.readlink( src.file ), dst.file )
181
182
			else:
183
				raise NotImplementedError()
184
185
		# src entity is missing
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
186
		elif src.type == '_':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
187
188
			# if dst entity doesn't exist, we're good
189
			if dst.type == '_':
190
				pass;
191
192
			# if dst entity is a file or symlink, delete it
23 by Tim Marston
implemented CopyInWalker in terms of CopyBaseWalker, changed implementation of
193
			elif dst.type == 'f' or dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
194
				self.print_op( rel_file, '_', '*', dst.type )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
195
				os.unlink( dst.file )
196
197
			# if dst entity is a directory, delete it
198
			elif dst.type == 'd':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
199
				self.print_op( rel_file, '_', '*', 'd' )
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
200
				shutil.rmtree( dst.file )
201
202
			else:
203
				raise NotImplementedError()
204
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
205
		# if we got here, we don't want to recurse...
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
206
		return False