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