patternpythonMinor
Pathname matching and listing program
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
should clarify how it works:
This would produce output such as:
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:
clear enough to be self-documenting.
on other Unices?
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
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 rulefilesshould 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 longhelp 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:
-
Instead of calling
-
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:
-
I hope this helps.
Edit: Regarding using a
Of course this is just a starting point. After some work in the script you might want to create a
- Please use docstrings.
- Note that
argparsecan 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 theloggingmodule (logging.error,logging.warning, etc.).
GLOBAL_verbosecould be replaced also usinglogging.debugand 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.sepcould be reduced topattern[-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.