HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Pathname matching and listing program

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
programpathnamelistingandmatching

Problem

This Python 3 program outputs a list of all pathnames in the filesystem
that match a list of rules taken from a file. You can add and remove
sets of pathnames.

The original purpose was to generate lists of pathnames to feed to
cpio for backups. A simplified version of one of my backup rulefiles
should clarify how it works:

# Entire home directory
+ /home/tom
# Remove hidden files and other junk
  • /home/tom/.*
  • /home/tom/java


# Then add back stuff that I do want
+ /home/tom/.bash*
# Remove stuff that goes in another backup
  • /home/tom/inactive


# Add stuff from another location
+ /large/bkups/patterns
# Remove junk
  • *~
  • *.o




This would produce output such as:

/home/tom
/home/tom/.bashrc
/home/tom/bin
/home/tom/bin/fpmr
/large/bkups/patterns
/large/bkups/patterns/current
/large/bkups/patterns/inactive


fpmr -h gives a short help text. fpmr -? would normally give a long
help text, but since it's quite long and the contents don't affect the
code, I removed it from the code posted below. I'll keep it available
here for a while:

http://tomzy.ch/code/fpmr_detailed_help.txt (this is now a dead link)

Aside from general code review, I'm particularly interested in the
following:

  • I use a couple of global flags. It seemed the best way. Thoughts?



  • Can anyone think of cases I've overlooked?



  • Does any code seem to need more comments? I find most Python code


clear enough to be self-documenting.

  • It was developed on Linux. Is there anything that wouldn't work right


on other Unices?

  • It lists regular files, directories, and symlinks. It ignores named


pipes, sockets, and device files. A Solaris filesystem can contain
something called a "door". Can any Solaris users verify that it will
ignore doors too?

`#!/usr/bin/env python3

import sys

try:
import argparse
except ImportError:
print('The "argparse" module is not available. '
'Use Python 3.2 or better.', file=sys.stderr)
sys.exit(1)

import fnmatch
import glob
import os
import os.pa

Solution

Some comments:

  • Please use docstrings.



  • Note that argparse can be installed from pypi for older python versions.



  • For the computation of relative paths, please have a look at os.path.relpath.



  • I don't like using global flags either. What about using a class to store that internal state?



  • Instead of vmsg, errwarn, etc., please use the logging module (logging.error, logging.warning, etc.).



  • GLOBAL_verbose could be replaced also using logging.debug and setting log level based on the command line argument.



-
Instead of calling main at the bottom of the script directly, please use the usual convention:

if __name__ == '__main__':
    main()


-
Try to parse arguments before calling main and pass them to main. This way, it will be testable if you want to write test cases in the future:

if __name__ == '__main__':
    args = process_args()
    main(args)


-
line[:1] == '#' could be reduced to line[0] == '#', but I find more readable this: line.startswith('#')

  • pattern[-1:] == os.sep could be reduced to pattern[-1] == os.sep, but I find more readable this: pattern.endswith(os.sep)



I hope this helps.

Edit: Regarding using a class instead of globals, what I meant more or less is following this pattern:

class Script(object):

    """Docstring."""

    def __init__(self, verbose, null):
        """Docstring."""
        self.verbose = verbose
        self.null = null

    ...
    def run(self):
        """Docstring."""
        

    ...

    def add_path(self, pset, setname, path):
        """Docstring."""
        ...
        if '\n' in path and not self.null:
            ...
        if self.verbose:
            ...

if __name__ == '__main__':
    args = process_args()
    script = Script(args.verbose, args.null)
    script.run()


Of course this is just a starting point. After some work in the script you might want to create a Rule class or a RuleProcessor class.

Code Snippets

if __name__ == '__main__':
    main()
if __name__ == '__main__':
    args = process_args()
    main(args)
class Script(object):

    """Docstring."""

    def __init__(self, verbose, null):
        """Docstring."""
        self.verbose = verbose
        self.null = null

    ...
    def run(self):
        """Docstring."""
        <put here the code in the original main function>

    ...

    def add_path(self, pset, setname, path):
        """Docstring."""
        ...
        if '\n' in path and not self.null:
            ...
        if self.verbose:
            ...


if __name__ == '__main__':
    args = process_args()
    script = Script(args.verbose, args.null)
    script.run()

Context

StackExchange Code Review Q#59851, answer score: 3

Revisions (0)

No revisions yet.