changeset 12:e0a3148e67a8

bug fix and a short overhaul of documentation
author Jeff Hammel <jhammel@mozilla.com>
date Mon, 28 Jan 2013 19:54:36 -0800
parents 03db23600c1f
children 6ffd095cf5dd
files README.txt commandparser/command.py setup.py tests/simpleexample.py
diffstat 4 files changed, 113 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/README.txt	Mon Apr 02 10:42:13 2012 -0700
+++ b/README.txt	Mon Jan 28 19:54:36 2013 -0800
@@ -1,8 +1,93 @@
 CommandParser
-===========
+=============
 
 change objects to OptionParser instances via reflection
 
+Overview
+--------
+
+It is a common pattern for command line interfaces to use subcomands (e.g.):
+
+  hg commit -m 'foo bar'
+  git push origin master
+
+CommandParser does this via introspection of a given class.  When
+invoked with a class, CommandParser uses the inspect module to pull
+out the mandatory and optional arguments for each of the class's
+methods, which are translated to subcommands, and make a OptionParser
+instance from them. ``%prog help`` will then display all of the
+subcommands and ``%prog help <subcommand>`` will give you help on the
+``<subcommand>`` chosen.  Methods beginning with an underscore (`_`)
+are passed over.  This gives an easy way to translate an API class
+into a command line program::
+
+  class Foo(object):
+    """silly class that does nothing"""
+    def __init__(self): pass
+    def foo(self, value):
+      print "The value is %s" % value
+    def bar(self, fleem, verbose=False):
+      """
+      The good ole `bar` command
+      - fleem: you know, that thing fleem
+      - verbose: whether to print out more things or not
+      """
+      if verbose:
+        print "You gave fleem=%s" % fleem
+      return fleem * 2
+
+  import commandparser
+  parser = commandparser.CommandParser(Foo)
+  parser.invoke()
+
+(From http://k0s.org/hg/CommandParser/file/tip/tests/simpleexample.py )
+
+Example invocation::
+
+  (paint)│./simpleexample.py help
+  Usage: simpleexample.py [options] command [command-options]
+  
+  silly class that does nothing
+  
+  Options:
+    -h, --help  show this help message and exit
+  
+  Commands: 
+    bar   The good ole `bar` command
+    foo   
+    help  print help for a given command
+  (paint)│./simpleexample.py foo
+  Usage: simpleexample.py foo <value>
+  
+  simpleexample.py: error: Not enough arguments given
+  (paint)│./simpleexample.py foo 4
+  The value is 4
+  (paint)│./simpleexample.py bar blah
+  blahblah
+
+For optional arguments, the type of the default value will be
+inspected from the function signature.  Currently, mandatory arguments
+are all strings, though this is clearly a shortcoming.
+
+The class docstring is used for ``%prog --help`` (and ``%prog help``,
+same thing). The method docstrings (including those of ``__init__``
+for global options) are used for subcommand help.  If the arguments
+are listed in the docstring in the form given above
+(``- <argument> : <something about the argument``) then these are used
+to provide help on the individual options.  Otherwise, these are left
+blank.
+
+For straight-forward cases, it may be enough to pass your class
+directly to the CommandParser constructor.  For more complex cases, it
+is an advisable pattern to create a new class (either via subclassing
+or e.g. rolling from scratch, as applicable) that is more amenable to
+CommandParser rather than modifying an (e.g.) API class to fit what
+CommandParser expects.  This allows the use of an object-oriented
+interface for subcommands without sacrificing your API class, and if
+you can subclass then there's really not much extra code to write.
+
+See http://k0s.org/hg/CommandParser/file/tip/tests for tests and examples.
+
 ----
 
 Jeff Hammel
--- a/commandparser/command.py	Mon Apr 02 10:42:13 2012 -0700
+++ b/commandparser/command.py	Mon Jan 28 19:54:36 2013 -0800
@@ -23,7 +23,6 @@
         self.default=default
 
 class CommandParser(OptionParser):
-    # TODO: add `help` command
 
     def __init__(self, _class, description=None):
         self._class = _class
@@ -165,16 +164,16 @@
             doc = cleandoc(function.__doc__)
         else:
             doc = ''
-        args, varargs, varkw, defaults = inspect.getargspec(function)
+        _args, varargs, varkw, defaults = inspect.getargspec(function)
         if defaults:
-            args = args[1:-len(defaults)]
-            optional = dict(zip(args[-len(defaults):], defaults))
+            args = _args[1:-len(defaults)]
+            optional = dict(zip(_args[-len(defaults):], defaults))
         else:
-            args = args[1:]
+            args = _args[1:]
             optional = None
         command = {'doc': doc,
                    'name': name,
-                   'args': args,
+                   'args': args, # mandatory arguments
                    'optional': optional,
                    'varargs': varargs
                    }
--- a/setup.py	Mon Apr 02 10:42:13 2012 -0700
+++ b/setup.py	Mon Jan 28 19:54:36 2013 -0800
@@ -4,7 +4,7 @@
 
 import os
 
-version = "0.1.3"
+version = "0.2"
 dependencies = []
 
 try:
@@ -39,7 +39,7 @@
       author='Jeff Hammel',
       author_email='jhammel@mozilla.com',
       url='http://k0s.org/hg/CommandParser',
-      license='',
+      license='MPL',
       packages=['commandparser'],
       include_package_data=True,
       zip_safe=False,
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/simpleexample.py	Mon Jan 28 19:54:36 2013 -0800
@@ -0,0 +1,20 @@
+#!/usr/bin/env python
+
+class Foo(object):
+    """silly class that does nothing"""
+    def __init__(self): pass
+    def foo(self, value):
+        print "The value is %s" % value
+    def bar(self, fleem, verbose=False):
+        """
+        The good ole `bar` command
+        - fleem: you know, that thing fleem
+        - verbose: whether to print out more things or not
+        """
+        if verbose:
+            print "You gave fleem=%s" % fleem
+        return fleem * 2
+
+import commandparser
+parser = commandparser.CommandParser(Foo)
+parser.invoke()