concrete realization of these goals. It exports a general FileVisitor class that mostly
just wraps os.walk for easier use and extension, as well as a generic SearchVisitor class
that generalizes the notion of directory searches.
By itself, SearchVisitor simply does what search_all did, but it also opens up the search
process to customization—bits of its behavior can be modified by overloading its
methods in subclasses. Moreover, its core search logic can be reused everywhere we
need to search. Simply define a subclass that adds extensions for a specific task. The
same goes for FileVisitor—by redefining its methods and using its attributes, we can
tap into tree search using OOP coding techniques. As is usual in programming, once
you repeat tactical tasks often enough, they tend to inspire this kind of strategic
thinking.
Example 6-18. PP4E\Tools\visitor.py
"""
####################################################################################
Test: "python ...\Tools\visitor.py dir testmask [string]". Uses classes and
subclasses to wrap some of the details of os.walk call usage to walk and search;
testmask is an integer bitmask with 1 bit per available self-test; see also:
visitor_*/.py subclasses use cases; frameworks should generally use__X pseudo
private names, but all names here are exported for use in subclasses and clients;
redefine reset to support multiple independent walks that require subclass updates;
####################################################################################
"""
import os, sys
class FileVisitor:
"""
Visits all nondirectory files below startDir (default '.');
override visit* methods to provide custom file/dir handlers;
context arg/attribute is optional subclass-specific state;
trace switch: 0 is silent, 1 is directories, 2 adds files
"""
def init(self, context=None, trace=2):
self.fcount = 0
self.dcount = 0
self.context = context
self.trace = trace
def run(self, startDir=os.curdir, reset=True):
if reset: self.reset()
for (thisDir, dirsHere, filesHere) in os.walk(startDir):
self.visitdir(thisDir)
for fname in filesHere: # for non-dir files
fpath = os.path.join(thisDir, fname) # fnames have no path
self.visitfile(fpath)
def reset(self): # to reuse walker
self.fcount = self.dcount = 0 # for independent walks
def visitdir(self, dirpath): # called for each dir
Visitor: Walking Directories “++” | 331