/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
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
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
32
	information.  The print_op method, derived in those classes, takes, in
33
	addition to the relative filename, a source file type, an operation, and a
34
	sestination file type.  Valid file types are f (file), l (symlink), d
35
	(directory) and _ (non-existant).  Valid operations are * (modify), = (skip:
36
	same), @ (skip: symlink substitute) and # (skip: ignored).
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
37
	"""
38
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
39
40
	def __init__( self ):
41
		self.check_src_symlinks = False
42
		self.check_dst_symlinks = False
43
		self.check_dst_ignores = False
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
44
45
46
	def process( self, rel_file, src, dst ):
47
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
48
		# ignore?
49
		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
50
			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
51
			return True
52
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
53
		# src entity is directory
54
		if src.type == 'd':
55
56
			# if dst entity doesn't exist, create directory (no need to recurse,
57
			# since we're copying the whole directory)
58
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
59
				self.print_op( rel_file, 'd', '*', '_' )
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
60
				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
61
62
			# if dst entity is a directory, copy permissions, as required (and
63
			# recurse)
64
			elif dst.type == 'd':
66 by Tim Marston
check directories for (permission) changes in copy base walker
65
				if os.stat( src.file ).st_mode != os.stat( dst.file ).st_mode:
66
					self.print_op( rel_file, 'd', '*', 'd' )
67
					shutil.copystat( src.file, dst.file )
68
				else:
69
					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
70
				return True
71
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
72
			# if dst entity is a symlink to a directory, and this is an
73
			# acceptable substitute, just recurse
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
74
			elif self.check_dst_symlinks and dst.link_type == 'd' and \
75
					the.config.symlinks.matches( rel_file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
76
				self.print_op( rel_file, 'd', '@', 'd' )
23 by Tim Marston
implemented CopyInWalker in terms of CopyBaseWalker, changed implementation of
77
				return True
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
78
79
			# if dst entity is a file or symlink in home dir, replace it with
80
			# directory (no need to recurse, since we're copying the whole
81
			# directory)
82
			elif dst.type == 'f' or dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
83
				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
84
				os.unlink( dst.file )
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
85
				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
86
87
			else:
88
				raise NotImplementedError()
89
90
		# src entity is file
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
91
		elif src.type == 'f':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
92
93
			# if dst entity doesn't exist, copy file
94
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
95
				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
96
				shutil.copy( src.file, dst.file )
97
				shutil.copystat( src.file, dst.file )
98
99
			# if dst entity is a file, replace it only if it differs
100
			elif dst.type == 'f':
101
				if not filecmp.cmp( src.file, dst.file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
102
					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
103
					os.unlink( dst.file )
104
					shutil.copy( src.file, dst.file )
105
					shutil.copystat( src.file, dst.file )
106
				else:
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
107
					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
108
109
			# if dst entity is a directory, replace it with file
110
			elif dst.type == 'd':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
111
				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
112
				shutil.rmtree( dst.file )
113
				shutil.copy( src.file, dst.file )
114
				shutil.copystat( src.file, dst.file )
115
116
			# if dst entity is a symlink, replace it with file
117
			elif dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
118
				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
119
				os.unlink( dst.file )
120
				shutil.copy( src.file, dst.file )
121
				shutil.copystat( src.file, dst.file )
122
123
			else:
124
				raise NotImplementedError()
125
126
		# src entity is a symlink
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
127
		elif src.type == 'l':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
128
129
			# if dst entity doesn't exist, copy symlink
130
			if dst.type == '_':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
131
				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
132
				os.symlink( os.readlink( src.file ), dst.file )
133
134
			# if dst entity is a symlink, replace it only if it differs
135
			elif dst.type == 'l':
136
				if os.readlink( src.file ) != os.readlink( dst.file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
137
					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
138
					os.unlink( dst.file )
139
					os.symlink( os.readlink( src.file ), dst.file )
140
				else:
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
141
					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
142
143
			# if dst entity is a file, replace it with symlink
144
			elif dst.type == 'f':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
145
				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
146
				os.unlink( dst.file )
147
				os.symlink( os.readlink( src.file ), dst.file )
148
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
149
			# 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
150
			elif dst.type == 'd':
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
151
152
				# if src entity is a symlink to a directory, and this is an
153
				# acceptable substitute, just recurse
154
				if self.check_src_symlinks and src.link_type == 'd' and \
155
						the.config.symlinks.matches( rel_file ):
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
156
					self.print_op( rel_file, 'd', '@', 'd' )
39 by Tim Marston
implemented ignore lists in copyer and made symlink substitution work only one
157
					return True
158
159
				# else replace it with a symlink
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
160
				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
161
				shutil.rmtree( dst.file )
162
				os.symlink( os.readlink( src.file ), dst.file )
163
164
			else:
165
				raise NotImplementedError()
166
167
		# src entity is missing
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
168
		elif src.type == '_':
20 by Tim Marston
moved funcitonality of copy-out walker in to base class and made copy-out walker
169
170
			# if dst entity doesn't exist, we're good
171
			if dst.type == '_':
172
				pass;
173
174
			# if dst entity is a file or symlink, delete it
23 by Tim Marston
implemented CopyInWalker in terms of CopyBaseWalker, changed implementation of
175
			elif dst.type == 'f' or dst.type == 'l':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
176
				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
177
				os.unlink( dst.file )
178
179
			# if dst entity is a directory, delete it
180
			elif dst.type == 'd':
61 by Tim Marston
added home directory change reporting to CopyOutWalker; added --quiet option to
181
				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
182
				shutil.rmtree( dst.file )
183
184
			else:
185
				raise NotImplementedError()
186
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
187
		# 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
188
		return False