Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Command line programs for busy developers

Command line programs for busy developers

Command line interface tools are the new web framework. In addition to the
Python standard library's getopt, optparse and argparse there is a bewildering
array of packages available on the Python Packaging Index. Packages like Click,
Cliff, Docopt, Invoke all provide different mechanics for creating command line
applications. When all you want to do is expose a command line and get back to
creating functionality, it’s hard to know which alternative to reach for.

This presentation will have two parts, the first half will be a brief survey
of popular modules. Strengths, weaknesses and design philosophies will be
compared by creating command line processing for the same example program.

The second half will go into more depth on a specific package, Begins. This
package was authored by the presenter to minimise the disruption developers
experience when breaking flow to implement command line processing. The
audience will be shown how a single line of code can create a complex command
line interface. It will also be shown how a handful of decorators from the
Begins API can eliminate boilerplate code and create powerful, complex
applications.

Aaron Iles

August 03, 2014
Tweet

Other Decks in Programming

Transcript

  1. Review Process • Minimal Twitter Client • 3 sub-commands •

    API tokens from command line options • API tokens from environment variables
  2. Review Criteria • Small API • Minimise disruption to workflow

    • Minimise boilerplate code • Feature rich command lines
  3. optparse • High level command line parser • Support for

    common option behaviours • Object based API
  4. optparse >>> parser = OptionParser() >>> parser.add_option("-f", “—file”, ... dest=“filename”,

    ... help=“write report to FILE”, ... metavar=“FILE”) >>> parser.add_option("-q", “--quiet", ... action=“store_false”, ... dest=“verbose", ... default=True, ... help="don't print status messages") >>> (options, args) = parser.parse_args()
  5. argparse • High level command line parser • Support for

    common option behaviours • Support for sub-commands • Object base API
  6. argparse >>> parser = argparse.ArgumentParser( ... description=‘Process some integers.') >>>

    parser.add_argument('integers', metavar='N', ... type=int, nargs=‘+', ... help=‘an integer for the accumulator') >>> parser.add_argument(‘--sum', ... dest=‘accumulate’, ... action=‘store_const’, ... const=sum, default=max, ... help=‘sum the integers') >>> args = parser.parse_args()
  7. cliff ✓ Many hook points during application lifecycle No abstraction

    of argparse Requires use of sub-commands Expect use of entry points
  8. docopt ✓ Easily create complex command line logic Multiple points

    of control New syntax language to understand Fewer option behaviours
  9. docopt """Minimal Twitter client. ! Usage: tw_docopt.py [options] (timeline|retweets) tw_docopt.py

    -h | --help ! Options: -h --help Show this screen. --version Show version. """ ! >>> args = docopt(__doc__, version="PyConAU")
  10. click >>> @click.command() ... @click.option(‘--count’, default=1, ... help=‘Number of greetings.')

    ... @click.option(‘--name’, prompt='Your name', ... help=‘The person to greet.') ... def hello(count, name): ... pass
  11. begins ✓ Simple but powerful API ✓ Reflection on function

    signature ✓ Replaces the use of __name__ Beta
  12. begins usage: app.py [-h] ! Does nothing! ! optional arguments:

    -h, --help show this help message and exit
  13. Default Values >>> @begin.start ... def run(name=‘Arther', ... quest=‘Holy Grail’,

    ... colour=‘blue’, ... *knights): ... "tis but a scratch!"
  14. Default Values usage: app.py [-h] [--name NAME] [--quest QUEST] [--colour

    COLOUR] [knights [knights ...]] ! tis but a scratch! ! positional arguments: knights ! optional arguments: -h, --help show this help message and exit --name NAME, -n NAME (default: Arthur) --quest QUEST, -q QUEST (default: Holy Grail) --colour COLOUR, -c COLOUR (default: blue)
  15. Annotations for help >>> @begin.start ... def run(name: 'What, is

    your name?', ... quest: 'What, is your quest?', ... colour: 'What, is your favourite colour?'): ... pass
  16. Annotations for help usage: app.py [-h] NAME QUEST COLOUR !

    positional arguments: NAME What, is your name? QUEST What, is your quest? COLOUR What, is your favourite colour? ! optional arguments: -h, --help show this help message and exit
  17. Boolean Flags usage: app.py [-h] [--enable] [--no-enable] [--disable] [--no-disable] !

    optional arguments: -h, --help show this help message and exit --enable (default: False) --no-enable --disable --no-disable (default: True)
  18. Sub-Commands >>> import begin >>> @begin.subcommand ... def name(answer): ...

    "What is your name?” ... >>> @begin.subcommand ... def quest(answer): ... "What is your quest?” ... >>> @begin.start ... def main(): ... pass
  19. Sub-Commands usage: app.py [-h] {name,quest} ... ! optional arguments: -h,

    --help show this help message and exit ! Available subcommands: {name,quest} name What is your name? quest What is your quest?
  20. Multiple Commands >>> @begin.subcommand ... def subcmd(): ... pass ...

    >>> @begin.start(cmd_delim='--') ... def main(): ... pass
  21. Configuration Files >>> import begin >>> @begin.start(config_file='.camelot.cfg') ... def run(name='Arther',

    quest='Holy Grail’, ... colour='blue', *knights): ... "tis but a scratch!"
  22. Type Casting >>> @begin.start ... @begin.convert(port=int, ... debug=begin.utils.tobool) ... def

    main(host=‘127.0.0.1', ... port=8080, ... debug=False): ... "Run web application"
  23. Logging usage: app.py [-h] [-v | -q] [--loglvl {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [--logfile

    LOGFILE] [--logfmt LOGFMT] [messages [messages ...]] ! positional arguments: messages ! optional arguments: -h, --help show this help message and exit -v, --verbose Increse logging output -q, --quiet Decrease logging output ! logging: Detailed control of logging output ! --loglvl {DEBUG,INFO,WARNING,ERROR,CRITICAL} Set explicit log level --logfile LOGFILE Ouput log messages to file --logfmt LOGFMT Log message format
  24. Tracebacks usage: app.py [-h] [--tracebacks] [--tbdir TBDIR] [messages [messages ...]]

    ! positional arguments: messages ! optional arguments: -h, --help show this help message and exit ! tracebacks: Extended traceback reports on failure ! --tracebacks Enable extended traceback reports --tbdir TBDIR Write tracebacks to directory