/stdhome

To get this branch, use:
bzr branch http://bzr.ed.am/stdhome

« back to all changes in this revision

Viewing changes to lib/stdhome/walker/walker.py

  • Committer: Tim Marston
  • Date: 2016-05-22 17:12:03 UTC
  • Revision ID: tim@ed.am-20160522171203-y2qa4rnz0233jyul
typo in comment

Show diffs side-by-side

added added

removed removed

 
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
 
23
import stdhome.the as the
 
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
 
 
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
 
 
42
                if the.verbose >= 3:
 
43
                        print "walking [%s]" % self.__class__.__name__
 
44
 
 
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
 
 
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 )
 
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
 
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.
 
70
                        if not recurse: skip = rel_file + os.sep
 
71
 
 
72
 
 
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 ):
 
86
                                self.link_type = self.type
 
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'
 
97
 
 
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
 
 
103
 
 
104
        @staticmethod
 
105
        def generate_walk_list( full_dir, rel_file = '', recurse = True ):
 
106
                """Returns a list of files and directories in full_dir, specified as relative
 
107
                files (relative to full_dir), breadth first.
 
108
 
 
109
                """
 
110
 
 
111
                # ignore some files
 
112
                static_ignores = [ '.stdhome', '.stdhomerc' ] + \
 
113
                                                 the.repo.vcs.ignored_files
 
114
                if rel_file in static_ignores:
 
115
                        return list()
 
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 []
 
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 ) ) )
 
130
                        return sorted( ret )
 
131
 
 
132
                # other kinds are invalid
 
133
                else:
 
134
                        raise RuntimeError(
 
135
                                'unknown/exotic file: %s' % full_file )