[oclug] my tree.py commentary
Tom Goulet
tomg at nova.yi.org
Tue Apr 10 08:05:23 EDT 2001
Hello,
What follows is me talking about the good and bad things about my
programming wars entry.
>#!/usr/bin/env python
># This program is licenced under the GNU GPL.
>from sys import argv
>from getopt import getopt
I like doing "from foo import bar" and later "bar" instead of
"import foo" and later "foo.bar". This method shows the reader which
functions are going to be used, and it saves me typing later in the
program. It has the disadvantage to being vulnerable to namespace
collisions (there is a string.join and an os.path.join, for example).
>from posix import listdir, lstat, readlink
>from posixpath import isdir, islink, isabs, exists
The proper way to do this is "import os". But I don't like "import os"
because it hides that I'm a posix user.
>from string import join
>from stat import ST_SIZE
There is no posixpath.lgetsize :-P.
>EMPTY = ' '
>DOWN = '| '
>RIGHT = '|--'
>DIAG = '`--'
>ARROW = '->'
#defines are good #defines are nice.
>def main(argv):
> optlist, args = getopt(argv[1:], 'ds')
For some reason args is always empty if I do the args before the
options.
> if args:
An empty list is false. I love using bare variables as truth values.
> new_pathname = args[0]
> else:
> new_pathname = '.'
> opts = {'d': 0, 's': 0}
> if ('-d', '') in optlist:
This obviously doesn't work if someone specifies an argument to the -d
option.
> opts['d'] = 1
> if ('-s', '') in optlist:
> opts['s'] = 1
> print new_pathname
> totals = [0, 1]
> tree_a_directory(new_pathname, opts, [], totals)
totals is an example of a mutable object, which is passed by reference
in Python. The variables in total are changed in place and are
available after the function returns.
> print
> if opts['d']:
> print "%i directories" % totals[1]
> else:
> print "%i directories, %i files" % (totals[1], totals[0])
>
>def tree_a_directory(pathname, opts, indent, totals):
> a_listing = []
> for entry in listdir(pathname):
Going through the list twice is ugly, but I need to discard entries and
sort them.
> if entry[0] != '.':
> if opts['d']:
> if isdir(pathname + '/' + entry):
I keep doing "pathname + '/' + entry", but chdir(2) is also possible
instead.
> a_listing.append(entry)
> else:
> a_listing.append(entry)
> a_listing.sort()
Python rules.
> for entry in a_listing:
> new_pathname = pathname + '/' + entry
> if indent:
> print join(indent),
string.join joins with a space in between by default, and I didn't want
to specify an empty string to it because it would look ugly, so I
cheated. I made the indent variables need a space to the right of them
for them to be printed out correctly, and I tested for an empty indent
before printing.
> if entry is a_listing[-1]:
> print DIAG,
Python "is" is object comparsion, two variable names can be references
to the same object, and "is" checks for that.
> else:
> print RIGHT,
Multiple "print" statements make multiple write(2) system calls, which
is inefficient. Another way would be to make a list of strings, append
them in order and later print out the joined list.
> if opts['s']:
> print "[%9s]" % lstat(new_pathname)[ST_SIZE],
I didn't know you could specify field widths for string conversions
before I had to find out to write this program.
> print entry,
> if islink(new_pathname):
My program does two stat(2)s per entry, which is inefficient. It would
be better to do one os.stat and compare the results at different times
later.
> print ARROW,
> linkname = readlink(new_pathname)
> if not isabs(linkname):
> link_pathname = pathname + '/' + linkname
More ugliness because I'm not chdir(2)ing.
> else:
> link_pathname = linkname
> if not exists(link_pathname):
> print '('+linkname+')'
> else:
> print linkname
> elif isdir(new_pathname):
All hail elif.
> print
> totals[1] = totals[1] + 1
> if entry is a_listing[-1]:
> indent.append(EMPTY)
> else:
> indent.append(DOWN)
Add to the indent according to what should be displayed when an entry in
this subdirectory needs to be printed out.
> tree_a_directory(new_pathname, opts, indent, totals)
My first recursive function. I was surprised when it worked.
> indent.pop()
Remove the last indent thing. This actually returns it, but the return
is ignored.
This append() ; recurse() ; pop() bit is the really neat part to me.
> else:
> print
> totals[0] = totals[0] + 1
My tree.py and tree(1) actually count files and directories differently,
I think the ambiguity is on symlinks.
>
>main(argv)
And thar she goes.
Bonus commentary...
getdents(2) sucks alot (although I have no idea how it could be done
better).
Eight column tabs! Eight column tabs!
Dave O'Neill's tree.pl is _so_ _wrong_.
In Dan York's tree.py:
from os.path import *
I didn't know you could do that. ("from foo.bar import"..)
I also found it interesting how similar most entries were, I guess we
found all found the logical way to do things.
TomG
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 232 bytes
Desc: not available
Url : http://tux.oclug.on.ca/pipermail/oclug/attachments/20010410/f71d6919/attachment.bin
More information about the OCLUG
mailing list