github
Advanced Search
  • Home
  • Pricing and Signup
  • Explore GitHub
  • Blog
  • Login

simonw / optfunc

  • Admin
  • Watch Unwatch
  • Fork
  • Your Fork
  • Pull Request
  • Download Source
    • 122
    • 11
  • Source
  • Commits
  • Network (11)
  • Issues (0)
  • Downloads (0)
  • Wiki (1)
  • Graphs
  • Branch: master

click here to add a description

click here to add a homepage

  • Branches (1)
    • master ✓
  • Tags (0)
Sending Request…
Enable Donations

Pledgie Donations

Once activated, we'll place the following badge in your repository's detail box:
Pledgie_example
This service is courtesy of Pledgie.

Syntactic sugar for creating Python command line scripts by introspecting a function definition — Read more

  cancel

http://simonwillison.net/2009/May/28/optfunc/

  cancel
  • Private
  • Read-Only
  • HTTP Read-Only

This URL has Read+Write access

Added support for stdout and stderr special arguments and documented them 
simonw (author)
Fri May 29 15:47:52 -0700 2009
commit  e3fa034a545ed94ac5a039cf5b170c7d0ee21b7b
tree    674ccc3d2dee422db2bf28d2af09381af3e70391
parent  eda71154f5a5d8ddab05d752d5ebf8fa896cb8e5
optfunc /
name age
history
message
file LICENSE.txt Thu May 28 16:25:56 -0700 2009 Added BSD license [simonw]
file README.txt Fri May 29 15:47:52 -0700 2009 Added support for stdout and stderr special arg... [simonw]
file demo.py Thu May 28 10:39:51 -0700 2009 Added shebang line to demo scripts [simonw]
file geocode.py Fri May 29 05:38:54 -0700 2009 optfunc.main() - like optfunc.run(), but it pee... [simonw]
file optfunc.py Fri May 29 15:47:52 -0700 2009 Added support for stdout and stderr special arg... [simonw]
file subcommands_demo.py Fri May 29 05:38:54 -0700 2009 optfunc.main() - like optfunc.run(), but it pee... [simonw]
file test.py Fri May 29 15:47:52 -0700 2009 Added support for stdout and stderr special arg... [simonw]
README.txt
optfunc
=======

Parse command line options in Python using function introspection.

Post feedback here: http://simonwillison.net/2009/May/28/optfunc/

I can never remember how to use any of Python's regular command line parsing
libraries.

optfunc uses introspection to make a Python function available as a command
line utility. It's syntactic sugar around optparse from the standard library.

Here's what the API looks like so far:

    import optfunc
    
    def upper(filename, verbose = False):
        "Usage: %prog <file> [--verbose] - output file content in uppercase"
        s = open(filename).read()
        if verbose:
            print "Processing %s bytes..." % len(s)
        print s.upper()
    
    if __name__ == '__main__':
        optfunc.run(upper)

And here's the resulting command-line interface:

    $ ./demo.py --help
    Usage: demo.py <file> [--verbose] - output file content in uppercase
    
    Options:
      -h, --help     show this help message and exit
      -v, --verbose  
    $ ./demo.py README.txt 
    OPTFUNC
    ...
    $ ./demo.py README.txt -v
    Processing 2049 bytes...
    OPTFUNC
    ...

If you don't mind relying on some stack inspecting magic, you can replace the 
__name__ == '__main__ idiom with the following:

    optfunc.main(upper)

If you like really short scripts, you can even use this function as a 
decorator:

    @optfunc.main
    def upper(filename):
        print open(filename).read().upper()

How arguments work
------------------

Non-keyword arguments are treated as required arguments - optfunc.run will 
throw an error if they number of arguments provided on the command line 
doesn't match the number expected by the function (unless @notstrict is used, 
see below).

Keyword arguments with defaults are treated as options. At the moment, only 
string and boolean arguments are supported. Other types are planned.

Consider the following:

    def geocode(s, api_key='', geocoder='google', list_geocoders=False):

's' is a required argument. api_key, geocoder and list_geocoders are all 
options, with defaults provided. Since list_geocoders has a boolean as its 
default it will be treated slightly differently (in optparse terms, it will 
store True if the flag is provided on the command line and False otherwise).

The command line options are derived from the parameter names like so:

    Options:
      -h, --help            show this help message and exit
      -l, --list-geocoders
      -a API_KEY, --api-key=API_KEY
      -g GEOCODER, --geocoder=GEOCODER

Note that the boolean --list-geocoders is a flag, not an option that sets a
value.

The short option is derived from the first letter of the parameter. If that 
character is already in use, the second character will be used and so on.

The long option is the full name of the parameter with underscores converted 
to hyphens.

If you want complete control over the name of the options, simply name your 
parameter as follows:

    def foo(q_custom_name=False):

This will result in a short option of -q and a long option of --custom-name.

Special arguments
-----------------

Arguments with the names 'stdin', 'stdout' or 'stderr' will be automatically 
passed the relevant Python objects, for example:
    
    #!/usr/bin/env python
    # upper.py
    import optfunc
    
    @optfunc.main
    def upper_stdin(stdin, stdout):
        stdout.write(stdin.read().upper())

Does the following:

    $ echo "Hello, world" | ./upper.py
    HELLO, WORLD

Subcommands
-----------

Some command line applications feature subcommands, with the first argument 
to the application indicating which subcommand should be executed.

optfunc has the beginnings of support for this - you can pass an array of 
functions to the optfunc.run() and the names of the functions will be used 
to select a subcommand based on the first argument:

    import optfunc
    
    def one(arg):
        print "One: %s" % arg
    
    def two(arg):
        print "Two: %s" % arg
    
    def three(arg):
        print "Three: %s" % arg
    
    if __name__ == '__main__':
        optfunc.run([one, two, three])

Usage looks like this:

    $ ./subcommands_demo.py    
    Unknown command: try 'one', 'two' or 'three'
    $ ./subcommands_demo.py one
    one: Required 1 arguments, got 0
    $ ./subcommands_demo.py two arg
    Two: arg

This approach is limited in that help can be provided for an individual option 
but not for the application as a whole. If anyone knows how to get optparse to
handle the subcommand pattern please let me know.

Decorators
----------

optfunc also supports two decorators for stuff I couldn't work out how to 
shoehorn in to a regular function definition. geocode.py shows them in action:

    @optfunc.notstrict
    @optfunc.arghelp('list_geocoders', 'list available geocoders and exit')
    def geocode(s, api_key='', geocoder='google', list_geocoders=False):
        # ...

@notstrict means "don't throw an error if one of the required positional 
arguments is missing" - in the above example we use this because we still want
the list_geocoders argument to work even if a string has not been provided.

@arghelp('arg-name', 'help text') allows you to provide help on individual 
arguments, which will then be displayed when --help is called.

TODO
----

* Support for different argument types (int, string, filehandle, choices)
* Special handling for 'stdin' as an argument name
* Proper unix error semantics (sys.exit(1) etc)
* Allow the function to be a generator, print iterations to stdout
* Support for *args (I don't think **kwargs makes sense for optfunc)
* Subcommands need to interact with --help better
Blog | Support | Training | Contact | API | Status | Twitter | Help | Security
© 2010 GitHub Inc. All rights reserved. | Terms of Service | Privacy Policy
Powered by the Dedicated Servers and
Cloud Computing of Rackspace Hosting®
Dedicated Server