This script is generally quite similar to bigfiles.py. The only
important difference is how the files are sorted: in this
case, they are sorted in descending order by modification
time, that is, in reverse chronological order.
As with the bigfiles.py script, we create a class that inherits
from PathInfo, and define a different .__cmp__() method to change the sorting
behavior. Like bigfiles.py, we also redefine the .__str__() method to change the format used
to display each file's information.
We'll call the derived class OldInfo.
So, here's the overall flow for processing each directory tree named on the command line.
Instantiate a class called OldReport that holds all the information
needed to generate our report. The class constructor
takes the name of the director as an argument. It
walks the directory tree, makes each thing it finds
into an OldInfo object, and
then sorts them.
Use the .genFiles() method of
the BigReport object to
generate the OldInfo objects
in reverse chronological order, printing each one as it
is generated.
This part is identical to the corresponding section of bigfiles.py.
#!/usr/bin/env python #================================================================ # oldfiles.py: Show files in reverse chron. order by mod. time. # For documentation in "literate programming" style, see: # http://www.nmt.edu/help/lang/python/examples/pathinfo/ #---------------------------------------------------------------- SCRIPT_NAME = "oldfiles.py" EXTERNAL_VERSION = "1.1" #================================================================ # Imports #---------------------------------------------------------------- import sys, os import pathinfo
Again, this section is pretty much identical to the main program of bigfiles.py.
# - - - - - m a i n - - - - -
def main():
"""Main program."""
print "=== %s %s ===" % (SCRIPT_NAME, EXTERNAL_VERSION)
#-- 1 --
# [ if sys.argv[1:] is empty ->
# dirList := [ "." ]
# else ->
# dirList := sys.argv[1:] ]
dirList = sys.argv[1:]
if len(dirList) == 0:
dirList = [ "." ]
#-- 2 --
# [ sys.stdout +:= reports listing files below each
# directory named in dirList, with files in reverse
# chronological order by modification time ]
for dirName in dirList:
#-- 2 body --
# [ dirName is a string ->
# sys.stdout +:= a report listing the files below
# directory (dirName), with files in reverse
# chronological order by modification time ]
report ( dirName )
This function generates the report for one directory subtree. The pathname of the directory is the argument.
# - - - r e p o r t - - -
def report ( dirName ):
"""Generate the report for one directory subtree.
[ dirName is a string ->
sys.stdout +:= a report listing the files below
directory (dirName), with files in reverse
chronological order by modification time ]
"""
#-- 1 -
# [ basePath := dirName's absolute path name
# sys.stdout +:= report heading showing dirName's real
# absolute path ]
basePath = os.path.abspath ( dirName )
print "\n === %s ===" % os.path.realpath ( dirName )
#-- 2 --
# [ oldReport := an OldReport object describing all the
# accessible files in directory tree (dirName) ]
oldReport = OldReport ( basePath )
#-- 3 --
# [ oldReport is an OldReport object ->
# sys.stdout +:= lines describing files in oldReport
# in chronological order by modification time ]
for oldInfo in oldReport.genFiles():
print oldInfo
This class is very similar to the BigInfo class in bigfiles.py see Section 7.4, “class BigInfo: The PathInfo
subclass”. It too inherits from the
PathInfo class.
#================================================================
# Functions and classes
#----------------------------------------------------------------
# - - - - - c l a s s O l d I n f o - - - - -
class OldInfo(pathinfo.PathInfo):
"""Represents information about one file; sorts by mod time.
"""
This constructor is similar to Section 7.5, “BigInfo.__init__(): Constructor”.
# - - - O l d I n f o . _ _ i n i t _ _ - - -
def __init__ ( self, path, basePath ):
"""Constructor for OldInfo."""
#-- 1 --
pathinfo.PathInfo.__init__ ( self, path )
#-- 2 --
self.__basePath = basePath
This method defines how comparison operators work when
comparing two OldInfo objects.
The returned value uses the same conventions as Python's
built-in cmp() function: it
returns a negative number if the first argument should
come first, a positive number if the second argument
should come first, and zero if they are considered equal.
To get this effect, we can simply call the cmp() function and pass it the two
timestamps. The result of that function would order the
objects in chronological order, so we could negate that result
to get reverse chronological order. Or, as is done here,
we could just reverse the order of the arguments.
# - - - O l d I n f o . _ _ c m p _ _ - - -
def __cmp__ ( self, other ):
"""Compare two OldInfo objects.
[ other is an OldInfo object ->
if self should precede other ->
return a negative number
else if self should follow other ->
return a positive number
else -> return 0 ]
"""
return cmp ( other.modEpoch, self.modEpoch )
This method is completely identical to the corresponding
method in the bigfiles.py script; see Section 7.7, “BigInfo.__str__(): String
conversion method”.
# - - - O l d I n f o . _ _ s t r _ _ - - -
def __str__ ( self ):
"""Format an OldInfo object for printing."""
#-- 1 --
# [ if self represents a directory ->
# suffix := "/"
# else ->
# suffix := "" ]
if self.isDir(): suffix = "/"
else: suffix = ""
#-- 2 --
# [ self.__basePath is the absolute path of a directory
# above self.path ->
# relPath := path to self.path relative to
# self.__basePath ]
absPath = os.path.abspath ( self.path )
relPath = absPath [ len(self.__basePath) + 1 : ]
#-- 3 --
if relPath == "":
relPath = "."
suffix = ""
#-- 4 --
return ( "%s %10s %s%s" %
(self.modTime(), self.size, relPath, suffix) )
This class is a container for a list of OldInfo objects. Because of the behavior
of the OldInfo.__cmp__(),
sorting this list puts the entries into reverse
chronological order.
The constructor takes the pathname of the directory
subtree as an argument, builds the list of OldInfo objects, and sorts them. You can
call the .genFiles() method and
it will generate the contained OldInfo objects in reverse chronological
order.
Again, this class is just about identical to the
corresponding class in the bigfiles.py script; see
Section 7.8, “class BigReport:
The class for the whole application”.
# - - - - - c l a s s O l d R e p o r t - - - - -
class OldReport:
"""Holds the old-files report.
Exports:
OldReport ( dir ):
[ dir is a string ->
if dir names a directory to which we have access ->
return an OldReport object describing all the
accessible files in that directory's subtree
else -> raise OSError ]
.genFiles():
[ generate a sequence of OldInfo objects representing
the files in self, in reverse chronological order
by modification timestamp ]
Class invariants:
.__oldList:
[ a list of information on all the files in self
as OldInfo objects, sorted ]
"""
The sole argument is the name of the directory. The
constructor creates an empty list .__oldList, fills it with OldInfo objects made from the directories
and files in the given subtree, and then sorts them.
For a more detailed description of the steps here, refer
to the nearly identical method, Section 7.9, “BigReport.__init__():
Constructor”.
# - - - O l d R e p o r t . _ _ i n i t _ _ - - -
def __init__ ( self, dir ):
"""Constructor for the OldReport class."""
#-- 1 --
self.__oldList = []
#-- 2 --
# [ dir is a string ->
# self.__oldList := self.__oldList with OldInfo
# objects added representing every accessible
# file in the subtree named by dir ]
os.path.walk ( dir, self.__visitor, dir )
#-- 3 --
# [ self.__oldList := self.__oldList, sorted ]
self.__oldList.sort()
Very similar to Section 7.10, “BigReport.__visitor():
Visitor function for os.path.walk()”.
# - - - O l d R e p o r t . _ _ v i s i t o r - - -
def __visitor ( self, basePath, dirName, nameList ):
"""Visitor function for os.path.walk.
[ (dirName is the name of a directory) and
(nameList is a list of the names within that
directory) ->
self.__oldList := self.__oldList with BigInfo
objects added representing the accessible
ordinary files in nameList ]
"""
#-- 1 --
# [ self.__oldList := self.__oldList with an OldInfo
# object added representing dirName ]
try:
dirInfo = OldInfo ( dirName, basePath )
self.__oldList.append ( dirInfo )
except OSError, detail:
pass
#-- 2 --
for fileName in nameList:
#-- 2 body --
# [ if fileName names an accessible regular file ->
# self.__oldList := self.__oldList with a new
# OldInfo object representing fileName
# else -> I ]
#-- 2.1 --
# [ filePath := dirName + fileName ]
filePath = os.path.join ( dirName, fileName )
#-- 2.2 --
# [ if filePath is an accessible path to a regular file ->
# self.__oldList := self.__oldList + (an OldInfo
# showing the status of filePath)
# else -> I ]
try:
oldInfo = OldInfo ( filePath, basePath )
if oldInfo.isFile():
self.__oldList.append ( oldInfo )
except OSError, detail:
pass
For a detailed narrative, see the identical method in
bigfiles.py: Section 8.11, “OldReport.genFiles():
Generate the report”.
# - - - O l d R e p o r t . g e n F i l e s - - -
def genFiles ( self ):
"""Generate the OldInfo objects in self.__oldList."""
for oldInfo in self.__oldList:
yield oldInfo
raise StopIteration