Pattern for foo tool <-> API <-> shell|GUI

Steven Bethard steven.bethard at gmail.com
Sun Mar 25 10:44:34 EDT 2007


Anastasios Hatzis wrote:
> I'm working on a tool which is totally command-line based and consisting of 
> multiple scripts. The user can execute a Python script in the shell, this 
> script does some basic verification before delegating a call into my tool's 
> package and depending on some arguments and options provided in the 
> command-line, e.g.
>     $python generate.py myproject --force --verbose
> the tool processes whatever necessary. There are multiple command handlers 
> available in this package which are responsible for different tasks and 
> depending of the script that has been executed one or more of these command 
> handlers are fired to do their work ;)

Side note: you might find argparse (http://argparse.python-hosting.com/) 
makes this a bit easier if you have positional arguments or sub-commands::

     >>> parser = argparse.ArgumentParser()
     >>> parser.add_argument('name')
     >>> parser.add_argument('--force', action='store_true')
     >>> parser.add_argument('--verbose', action='store_true')
     >>> parser.parse_args(['my_project', '--force', '--verbose'])
     Namespace(force=True, name='my_project', verbose=True)

     >>> parser = argparse.ArgumentParser()
     >>> subparsers = parser.add_subparsers()
     >>> cmd1_parser = subparsers.add_parser('cmd1')
     >>> cmd1_parser.add_argument('--foo')
     >>> cmd2_parser = subparsers.add_parser('cmd2')
     >>> cmd2_parser.add_argument('bar')
     >>> parser.parse_args(['cmd1', '--foo', 'X'])
     Namespace(foo='X')
     >>> parser.parse_args(['cmd2', 'Y'])
     Namespace(bar='Y')

> And I don't think that this is very trivial (at least not for my programming 
> skill level). In the given example "generate.py" (above) the following 
> scenario is pretty likely:
> 
> (1) User works with UML tool and clicks in some dialog a "generate" button
> (2) UML tool triggers this event an calls a magic generate() method of my tool 
> (via the API I provide for this purpose), like my generate.py script would do 
> same way
> (3) Somewhen with-in this generate process my tool may need to get some 
> information from the user in order to continue (it is in the nature of the 
> features that I can't avoid this need of interaction in any case).

So you're imagining an API something like::

     def generate(name,
                  force=False,
                  verbose=False,
                  handler=command_line_handler):
         ...
         choice = handler.prompt_user(question_text, user_choices)
         ...

where the command-line handler might look something like::

     class CommandLineHandler(object):
         ...
         def prompt_user(self, question_text, user_choices):
             while True:
                 choice = raw_input(question_text)
                 if choice in user_choices:
                     return choice
                 print 'invalid choice, choose from %s' % choices

and the GUI client would implement the equivalent thing with dialogs? 
That seems basically reasonable to me, though you should be clear in the 
documentation of generate() -- and any other methods that accept handler 
objects -- exactly what methods the handler must provide.

You also may find that "prompt_user" is a bit too generic -- e.g. a file 
chooser dialog looks a lot different from a color chooser dialog -- so 
you may need to split this up into "prompt_user_file", 
"prompt_user_color", etc. so that handler's don't have to introspect the 
question text to know what to do...

STeVe



More information about the Python-list mailing list