[Tutor] Script Feedback

Steven D'Aprano steve at pearwood.info
Tue Mar 30 18:57:54 CEST 2010


On Wed, 31 Mar 2010 01:27:43 am Damon Timm wrote:

[...]
> My initial questions are:
>
> 1. Is there a better way to implement a --quiet flag?

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)


> 2. I am not very clear on the use of Exceptions (or even if I am
> using it in a good way here) — is what I have done the right
> approach? 

Hmmm... perhaps.

Usually, you want to catch an exception in order to try an alternative 
approach, e.g.:

try:
    result = somefunction(x)
except ValueError:
    # Fall back on some other approach.
    result = "something else"

Occasionally, you want to catch an exception just to ignore it. It's 
generally *not* a good idea to catch an exception just to print an 
error message and then exit, as you do. Just let the exception 
propagate, and Python will print a rich and detailed traceback together 
with your error message.

However, a reasonable exception (pun intended) for that rule is to hide 
the traceback from the users, who probably can't do anything about it, 
and would only get confused by the details. So you want to have your 
functions and classes raise exceptions, and the application layer (the 
user interface) catch them.

So I would do something like this (untested). In your function code:

    ...
    if os.path.exists(tar_name):
        msg = "A tar file already exists this this directory name." \
        " Move or rename it and try again."
        raise DestinationTarFileExists(msg)


Then your application layer looks something like:


if __name__ == '__main__':
    try:
        ...
    except KeyboardInterrupt:
        sys.exit(1)
    except DestinationTarFileExists, e:
        print e.message
        sys.exit(2)
    # Any other exception is unexpected, so we allow Python
    # to print the full traceback as normal.



> 3. Finally, in general: any feedback on how to improve 
> this? (I am thinking, just now, that the script is only suitable for
> a command line usage, and couldn’t be imported by another script, for
> example.)

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.

E.g.:

def tar_bzip2_directories(directories, callback=None):
    for directory in directories:
        file_name = '-'.join(directory.split(' '))
        tar_name = file_name.replace('/','').lower() + ".tar.bz2"
        if os.path.exists(tar_name):
            raise DestinationTarFileExists(errmsg)
        if callback is not None:
            callback(directory, filename, tarname)
        ...


Create a main function that runs your application:


def main(argv=None, callback=None):
    if argv is None:
        argv = sys.argv
    process_command_line_options(argv)
    if callback is None:
        def callback(dirname, filename, tarname):
            print "Processing ..."
    tar_bzip2_directories(...)


if __name__ == '__main__':
    try:
        main()
    except ...  # as above


Now anyone can import the module and call individual functions, or even 
call main, or they can run it as a script.


Hope this helps,



-- 
Steven D'Aprano


More information about the Tutor mailing list