Next / Previous / Contents / TCC Help System / NM Tech homepage

8. Source code for oldfiles.py

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.

  1. 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.

  2. Use the .genFiles() method of the BigReport object to generate the OldInfo objects in reverse chronological order, printing each one as it is generated.

8.1. oldfiles.py: Code prologue

This part is identical to the corresponding section of bigfiles.py.

oldfiles.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

8.2. oldfiles.py: The main program

Again, this section is pretty much identical to the main program of bigfiles.py.

oldfiles.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 )

8.3. report(): Generate one directory tree's report

This function generates the report for one directory subtree. The pathname of the directory is the argument.

oldfiles.py
# - - -   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

8.4. class OldInfo: The PathInfo subclass

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.

oldfiles.py
#================================================================
# 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.
    """

8.5. OldInfo.__init__()

This constructor is similar to Section 7.5, “BigInfo.__init__(): Constructor”.

oldfiles.py
# - - -   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

8.6. OldInfo.__cmp__(): The comparator method

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.

oldfiles.py
# - - -   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 )

8.7. OldInfo.__str__(): String conversion method

This method is completely identical to the corresponding method in the bigfiles.py script; see Section 7.7, “BigInfo.__str__(): String conversion method”.

oldfiles.py
# - - -   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) )

8.8. class OldReport: The application class

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”.

oldfiles.py
# - - - - -   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 ]
    """

8.9. OldReport.__init__(): Constructor

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”.

oldfiles.py
# - - -   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()

8.10. OldReport.__visitor(): Visitor function for os.path.walk()

Very similar to Section 7.10, “BigReport.__visitor(): Visitor function for os.path.walk().

oldfiles.py
# - - -   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

8.11. OldReport.genFiles(): Generate the report

For a detailed narrative, see the identical method in bigfiles.py: Section 8.11, “OldReport.genFiles(): Generate the report”.

oldfiles.py
# - - -   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

8.12. Epilogue

These lines call main(), assuming that this is run as a script.

oldfiles.py
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

if  __name__  ==  "__main__":
    main()