/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
1
# walker.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 os
43 by Tim Marston
fixed error in walker.py (from last checkin)
23
import stdhome.the as the
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
24
25
26
class Walker:
27
	"""Classes which derive from the Walker class are intended to walk through
28
	(traverse) a single list of filenames in two locations.  They must provide
29
	the two locations (src_dir and dst_dir) and a list of relative filenames
30
	(walk_list).  For each files in the walk list, the process() method is
31
	called, allowing the deriving class to perform some kind of processing.  See
32
	process() for more information.
33
	"""
34
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
35
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
36
	def walk( self ):
37
		"""Iterates over self.walk_list, calling process() for each entry in turn.  For
38
		directory entries, where process() returns False, subsequent entries in
39
		walk_list that fall under the directory are skipped.
40
		"""
41
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
42
		if the.verbose >= 3:
88 by Tim Marston
python3ification
43
			print("walking [%s]" % self.__class__.__name__)
77 by Tim Marston
added -vvv logging of walker execution
44
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
45
		skip = ''
46
		for rel_file in self.walk_list:
47
48
			# if we're skipping, skip entries in subdirectories, or turn off
49
			# skipping if it no longer matches
50
			if skip:
51
				if skip == rel_file[ : len( skip ) ]:
52
					continue
53
				else:
54
					skip = ''
55
16 by Tim Marston
walker now passes Walker.File objects to process(), which includes file name,
56
			src = Walker.File( os.path.join( self.src_dir, rel_file ) )
57
			dst = Walker.File( os.path.join( self.dst_dir, rel_file ) )
58
59
			# process ths entry
60
			recurse = self.process( rel_file, src, dst )
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
61
62
			# Set up skipping, as required.  Note that we don't check to see if
63
			# we're dealing with a directory here.  We can't, because we've no
64
			# way of knowing what to check.  It could be src_type or dst_type
65
			# (if src_dir or dst_dir was used to generate the walk list) or it
66
			# could be neither (if the walk list came from somewhere else).  But
77 by Tim Marston
added -vvv logging of walker execution
67
			# it shouldn't matter: we add a path seperateor (os.sep) to the end
68
			# of the filename, so it wuill only match files that are descendents
69
			# of a directory with the name of this file.
21 by Tim Marston
fixed bug preventing skipping of subdirectories in walker; also added
70
			if not recurse: skip = rel_file + os.sep
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
71
72
16 by Tim Marston
walker now passes Walker.File objects to process(), which includes file name,
73
	class File:
74
75
		def __init__( self, full_file ):
76
			self.file = full_file
77
			if not os.path.exists( self.file ):
78
				self.type = '_'
79
			elif os.path.isfile( self.file ):
80
				self.type = 'f'
81
			elif os.path.isdir( self.file ):
82
				self.type = 'd'
83
			else:
84
				self.type = '?'
85
			if os.path.islink( self.file ):
27 by Tim Marston
got symlink accept lists working; fixed some walker bugs
86
				self.link_type = self.type
16 by Tim Marston
walker now passes Walker.File objects to process(), which includes file name,
87
				self.type = 'l'
88
			else:
89
				self.link_type = False
90
91
		def get_type_name( self ):
92
			if self.type == 'l': return 'symlink'
93
			elif self.type == 'f': return 'file'
94
			elif self.type == 'd': return 'directory'
95
			elif self.type == '_': return 'missing'
96
			else: return 'unknown'
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
97
21 by Tim Marston
fixed bug preventing skipping of subdirectories in walker; also added
98
		def __str__( self ):
99
			type = self.type
100
			if( self.link_type ): type += '/' + self.link_type
101
			return 'File( %s (%s) )' % ( self.file, type )
102
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
103
104
	@staticmethod
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
105
	def generate_walk_list( full_dir, rel_file = '', recurse = True ):
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
106
		"""Returns a list of files and directories in full_dir, specified as relative
107
		files (relative to full_dir), breadth first.
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
108
5 by Tim Marston
moved copy-in, copy-out and deployment conflict checking to a set of "walkers";
109
		"""
110
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
111
		# ignore some files
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
112
		static_ignores = [ '.stdhome', '.stdhomerc' ] + \
113
						 the.repo.vcs.ignored_files
114
		if rel_file in static_ignores:
16 by Tim Marston
walker now passes Walker.File objects to process(), which includes file name,
115
			return list()
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
116
117
		full_file = os.path.join( full_dir, rel_file )
118
119
		# files and links are returned
120
		if os.path.isfile( full_file ) or os.path.islink( full_file ):
121
			return [ rel_file ]
122
123
		# directories are returned and recursed in to
124
		elif os.path.isdir( full_file ):
125
			ret = [ rel_file ] if rel_file != '' else []
82 by Tim Marston
added general reporting to CopyBase and configured it via copy-in and copy-out
126
			if recurse:
127
				for file in os.listdir( full_file ):
128
					ret.extend( Walker.generate_walk_list(
129
						full_dir, os.path.join( rel_file, file ) ) )
8 by Tim Marston
added diff command; moved all command to commands subdir; made stage-revert
130
			return sorted( ret )
131
132
		# other kinds are invalid
133
		else:
134
			raise RuntimeError(
135
				'unknown/exotic file: %s' % full_file )