[Tutor] Script Feedback

Damon Timm damontimm at gmail.com
Wed Mar 31 01:54:34 CEST 2010


Hello Denis & Steven -

Thanks for your replies.  I have taken another stab at things to try
and bring it a little further up to snuff ... some more
comments/thoughts follow ...

On Tue, Mar 30, 2010 at 12:57 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> I usually create a function "print_" or "pr", something like this:
>
> def print_(obj, verbosity=1):
>    if verbosity > 0:
>        print obj
>
> and then have a variable "verbosity" which defaults to 1 and is set to 0
> if the user passes the --quiet flag. Then in my code, I write:
>
> print_("this is a message", verbosity)

Your suggestion prompted me to remember having looked into this
earlier (and found an old thread of mine) -- some folks had
recommended using the logging module -- which I have implemented in
round two (seems to work).  I think it accomplishes the same thing
that you are suggesting, only using one of Python's built-ins.

On Tue, Mar 30, 2010 at 12:57 PM, Steven D'Aprano <steve at pearwood.info> wrote:
> Separate the underlying functionality from the application-level code.
> These functions should NEVER print anything: they do all communication
> through call-backs, or by returning a value, or raising an exception.

I tried to implement this, however, I am not sure how the 'callback'
works ... is that just a function that a user would pass to *my*
function that gets called at the end of the script?  Also, I tried to
separate out the logic a little so the functions make more sense ... I
think I may remove the 'ignore_walk' function and just add it to the
tar_bz2_directory function (see below) ... but am still unclear about
the callback concept.

2010/3/30 spir ☣ <denis.spir at gmail.com>:
> First, it seems you use [:] only to preserves the object identity so that it remains a generator. But it may be better (at least clearer for me) to filter and transform the generation process so as to get what you actually need, I guess: iterating on (dirpath,filename) pairs. If I'm right on this, maybe try to figure out how do that.
> I would call the func eg "filtered_dir_walk" or "relevant_dir_walk".

I am not sure where I first got this 'ignore_walk' bit but I do
remember taking it from another program of mine ... to be honest,
though, I am rethinking its use and may implement it using fnmatch
testing so that I may implement wildcards (eg, *.pyc) ... right now,
it won't match wildcards and that might be helpful.

Again, thank you both for your feedback.  I made some changes tonight
(posted below) and also updated the changes on:
http://blog.damontimm.com/python-script-clean-bzip/ (if you want
pretty colors).

Damon

CODE BELOW
-------------------

#! /usr/bin/env python

'''Script to perform a "clean" bzip2 on a directory (or directories).  Removes
extraneous files that are created by Apple/AFP/netatalk before compressing.
'''

import os
import tarfile
import logging
from optparse import OptionParser

# Default files and directories to exclude from the bzip tar
IGNORE_DIRS = ('.AppleDouble',)
IGNORE_FILES = ('.DS_Store',)

class DestinationTarFileExists(Exception):
    '''If the destination tar.bz2 file already exists.'''

def ignore_walk(directory, ignore_dirs=None, ignore_files=None):
    '''Ignore defined files and directories when doing the walk.'''

    # TODO: this does not currently take wild cards into account.  For example,
    # if you wanted to exclude *.pyc files ... should fix that.  Perhaps
    # consider moving this entirely into the below function (or making it more
    # reusable for other apps).
    for dirpath, dirnames, filenames in os.walk(directory):
        if ignore_dirs:
            dirnames[:] = [dn for dn in dirnames if dn not in ignore_dirs]
        if ignore_files:
            filenames[:] = [fn for fn in filenames if fn not in ignore_files]
        yield dirpath, dirnames, filenames

def tar_bzip2_directory(directory, ignore_dirs=IGNORE_DIRS,
                                   ignore_files=IGNORE_FILES ):
    '''Takes a directory and creates a tar.bz2 file (based on the directory
    name).  You can exclude files and sub-directories as desired.'''

    file_name = '-'.join(directory.split(' '))
    tar_name = file_name.replace('/','').lower() + ".tar.bz2"

    if os.path.exists(tar_name):
        msg = ("The file %s already exists. " +
                "Please move or rename it and try again.") % tar_name
        raise DestinationTarFileExists(msg)

    tar = tarfile.open(tar_name, 'w:bz2')

    for dirpath, dirnames, filenames in ignore_walk(directory, ignore_dirs,
            ignore_files):
        for file in filenames:
            logging.info(os.path.join(dirpath, file))
            tar.add(os.path.join(dirpath, file))

    tar.close()

def main(args=None, callback=None):
    directories = []

    for arg in args:
        if os.path.isdir(arg):
            directories.append(arg)
        else:
            logging.ERROR("Ingoring: %s (it's not a directory)." % arg)

    for dir in directories:
        try:
            tar_bzip2_directory(dir)
        except DestinationTarFileExists, e:
            print e

if __name__ == "__main__":

    parser = OptionParser(usage="%prog [options: -q ] [dir1] [...dir2]")
    parser.add_option("-q", "--quiet", action="store_true", dest="quiet")
    options, args = parser.parse_args()

    if options.quiet:
        logging.basicConfig(level=logging.ERROR, format='%(message)s')
    else:
        logging.basicConfig(level=logging.INFO, format='%(message)s')

    main(args)


More information about the Tutor mailing list