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