/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: 2014-02-12 21:51:49 UTC
  • Revision ID: tim@ed.am-20140212215149-msaxl7vo98il5i4a
added more commands

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
 
 
24
 
 
25
 
class Walker:
26
 
        """Classes which derive from the Walker class are intended to walk through
27
 
        (traverse) a single list of filenames in two locations.  They must provide
28
 
        the two locations (src_dir and dst_dir) and a list of relative filenames
29
 
        (walk_list).  For each files in the walk list, the process() method is
30
 
        called, allowing the deriving class to perform some kind of processing.  See
31
 
        process() for more information.
32
 
        """
33
 
 
34
 
        def walk( self ):
35
 
                """Iterates over self.walk_list, calling process() for each entry in turn.  For
36
 
                directory entries, where process() returns False, subsequent entries in
37
 
                walk_list that fall under the directory are skipped.
38
 
                """
39
 
 
40
 
                skip = ''
41
 
 
42
 
                for rel_file in self.walk_list:
43
 
 
44
 
                        # if we're skipping, skip entries in subdirectories, or turn off
45
 
                        # skipping if it no longer matches
46
 
                        if skip:
47
 
                                if skip == rel_file[ : len( skip ) ]:
48
 
                                        continue
49
 
                                else:
50
 
                                        skip = ''
51
 
 
52
 
                        src_file = os.path.join( self.src_dir, rel_file )
53
 
                        dst_file = os.path.join( self.dst_dir, rel_file )
54
 
 
55
 
                        src_type = Walker.get_file_type( src_file )
56
 
                        dst_type = Walker.get_file_type( dst_file )
57
 
 
58
 
                        recurse = self.process(
59
 
                                rel_file, src_file, src_type, dst_file, dst_type )
60
 
 
61
 
                        # Set up skipping, as required.  Note that we don't check to see if
62
 
                        # we're dealing with a directory here.  We can't, because we've no
63
 
                        # way of knowing what to check.  It could be src_type or dst_type
64
 
                        # (if src_dir or dst_dir was used to generate the walk list) or it
65
 
                        # could be neither (if the walk list came from somewhere else).  But
66
 
                        # it shouldn't matter.  We adding an os.pathset to the end of the
67
 
                        # filename, so it wuill only match files that are descendents of a
68
 
                        # directory with the name of this file.
69
 
                        if not recurse: skip = rel_file + os.pathsep
70
 
 
71
 
 
72
 
        @staticmethod
73
 
        def get_file_type( full_file ):
74
 
                """Returns the type of a given file, at the time of calling.  Types are 'd' for
75
 
                directory, 'f' for file, 'l' for symlink, '_' for missing and '?' for
76
 
                anything else.
77
 
                """
78
 
 
79
 
                if not os.path.lexists( full_file ):
80
 
                        return '_'
81
 
                elif os.path.islink( full_file ):
82
 
                        return 'l'
83
 
                elif os.path.isfile( full_file ):
84
 
                        return 'f'
85
 
                elif os.path.isdir( full_file ):
86
 
                        return 'd'
87
 
                else:
88
 
                        return '?'
89
 
 
90
 
 
91
 
        @staticmethod
92
 
        def generate_walk_list( full_dir, rel_file = '' ):
93
 
                """Returns a list of files and directories in full_dir, specified as relative
94
 
                files (relative to full_dir), breadth first.
95
 
                """
96
 
 
97
 
                # ignore some files
98
 
                if rel_file in { '.bzr', '.stdhome' }: return list()
99
 
 
100
 
                full_file = os.path.join( full_dir, rel_file )
101
 
 
102
 
                # files and links are returned
103
 
                if os.path.isfile( full_file ) or os.path.islink( full_file ):
104
 
                        return [ rel_file ]
105
 
 
106
 
                # directories are returned and recursed in to
107
 
                elif os.path.isdir( full_file ):
108
 
                        ret = [ rel_file ] if rel_file != '' else []
109
 
                        for file in os.listdir( full_file ):
110
 
                                ret.extend( Walker.generate_walk_list(
111
 
                                        full_dir, os.path.join( rel_file, file ) ) )
112
 
                        return sorted( ret )
113
 
 
114
 
                # other kinds are invalid
115
 
                else:
116
 
                        raise RuntimeError(
117
 
                                'unknown/exotic file: %s' % full_file )
118
 
 
119
 
 
120
 
        @staticmethod
121
 
        def name_of_type( type ):
122
 
                if type == 'd': return 'a directory'
123
 
                elif type == 'f': return 'a file'
124
 
                elif type == 'l': return 'a symlink'
125
 
                elif type == '_': return 'missing'
126
 
                else: return 'something exotic'