dictionary type handler
authorAlexander Solovyov <alexander@solovyov.net>
Wed, 05 Jan 2011 20:41:43 +0100
changeset 158 c3ab9c848675
parent 157 398576a5d0c5
child 159 053f4635877b
dictionary type handler
docs/overview.rst
opster.py
tests/opster.t
tests/test_opts.py
--- a/docs/overview.rst	Wed Jan 05 20:17:20 2011 +0100
+++ b/docs/overview.rst	Wed Jan 05 20:41:43 2011 +0100
@@ -34,6 +34,8 @@
  - integer: value is convert to integer
  - string: value is passed as is
  - list: value is appended to this list
+ - dictionary: value is then assumed being in format ``key=value`` and is
+   then assigned to this dictionary
  - boolean/None: ``not default`` is passed and option takes no value
 
 Usage
--- a/opster.py	Wed Jan 05 20:17:20 2011 +0100
+++ b/opster.py	Wed Jan 05 20:41:43 2011 +0100
@@ -2,7 +2,7 @@
 '''Command line arguments parser
 '''
 
-import sys, traceback, getopt, types, textwrap, inspect, os
+import sys, traceback, getopt, types, textwrap, inspect, os, copy
 from itertools import imap
 
 __all__ = ['command', 'dispatch']
@@ -192,11 +192,7 @@
             write('\ncommands:\n\n')
             for cmd in hlplist:
                 doc = hlp[cmd]
-                if False:  # verbose?
-                    write(' %s:\n     %s\n' % (cmd.replace('|', ', '), doc))
-                else:
-                    write(' %-*s  %s\n' % (maxlen, cmd.split('|', 1)[0],
-                                              doc))
+                write(' %-*s  %s\n' % (maxlen, cmd.split('|', 1)[0], doc))
 
         if not cmdtable:
             return err('No commands specified!\n')
@@ -251,6 +247,8 @@
         write(''.join(help_options(options)))
 
 def help_options(options):
+    '''Generator for help on options
+    '''
     yield 'options:\n\n'
     output = []
     for o in options:
@@ -309,8 +307,8 @@
         defmap[pyname] = default
 
         # copy defaults to state
-        if isinstance(default, list):
-            state[pyname] = default[:]
+        if isinstance(default, (list, dict)):
+            state[pyname] = copy.copy(default)
         elif hasattr(default, '__call__'):
             funlist.append(pyname)
             state[pyname] = None
@@ -319,8 +317,10 @@
 
         # getopt wants indication that it takes a parameter
         if not (default is None or default is True or default is False):
-            if short: short += ':'
-            if name: name += '='
+            if short:
+                short += ':'
+            if name:
+                name += '='
         if short:
             shortlist += short
         if name:
@@ -335,8 +335,15 @@
         if t is types.FunctionType:
             del funlist[funlist.index(name)]
             state[name] = defmap[name](val)
-        elif t is types.ListType:
+        elif t is list:
             state[name].append(val)
+        elif t is dict:
+            try:
+                k, v = val.split('=')
+            except ValueError:
+                raise ParseError(name, "wrong definition: '%s' "
+                                 "(should be in format KEY=VALUE)" % val)
+            state[name][k] = v
         elif t in (types.NoneType, types.BooleanType):
             state[name] = not defmap[name]
         else:
@@ -470,7 +477,7 @@
             (e.args[0], ' '.join(e.args[1])))
         raise Abort()
     except ParseError, e:
-        err('%s: %s\n' % (e.args[0], e.args[1]))
+        err('%s: %s\n\n' % (e.args[0], e.args[1].strip()))
         help_func(e.args[0])
         raise Abort()
     except getopt.GetoptError, e:
@@ -496,6 +503,8 @@
     return inner
 
 def call_cmd_regular(func, opts):
+    '''Wrapper for command for handling function calls from Python
+    '''
     def inner(*args, **kwargs):
         funcargs, _, varkw, defaults = inspect.getargspec(func)
         if len(args) > len(funcargs):
--- a/tests/opster.t	Wed Jan 05 20:17:20 2011 +0100
+++ b/tests/opster.t	Wed Jan 05 20:41:43 2011 +0100
@@ -79,30 +79,61 @@
 
   $ run test_opts.py
   another: invalid arguments
+  
   test_opts.py [-l HOST] DIR
   
   Command with option declaration as keyword arguments
   
   options:
   
-   -l --listen     ip to listen on (default: localhost)
-   -p --port       port to listen on (default: 8000)
-   -d --daemonize  daemonize process
-      --pid-file   name of file to write process ID to
-   -t --test       testing help for a function (default: test)
-   -h --help       show help
+   -l --listen       ip to listen on (default: localhost)
+   -p --port         port to listen on (default: 8000)
+   -d --daemonize    daemonize process
+      --pid-file     name of file to write process ID to
+   -D --definitions  just some definitions
+   -t --test         testing help for a function (default: test)
+   -h --help         show help
 
 
-Yeah, I've got it, I should supply some argument::
+Yeah, I've got it, I should supply some arguments::
 
-  $ run test_opts.py right-here
+  $ run test_opts.py -d -p 5656 --listen anywhere right-here
+  {'daemonize': True,
+   'definitions': {},
+   'dirname': 'right-here',
+   'listen': 'anywhere',
+   'pid_file': '',
+   'port': 5656,
+   'test': 'test'}
+
+Now let's test our definitions::
+
+  $ run test_opts.py -D a=b so-what?
   {'daemonize': False,
-   'dirname': 'right-here',
+   'definitions': {'a': 'b'},
+   'dirname': 'so-what?',
    'listen': 'localhost',
    'pid_file': '',
    'port': 8000,
    'test': 'test'}
 
+  $ run test_opts.py -D can-i-haz fail?
+  definitions: wrong definition: 'can-i-haz' (should be in format KEY=VALUE)
+  
+  test_opts.py [-l HOST] DIR
+  
+  Command with option declaration as keyword arguments
+  
+  options:
+  
+   -l --listen       ip to listen on (default: localhost)
+   -p --port         port to listen on (default: 8000)
+   -d --daemonize    daemonize process
+      --pid-file     name of file to write process ID to
+   -D --definitions  just some definitions
+   -t --test         testing help for a function (default: test)
+   -h --help         show help
+
 
 Should we check passing some invalid arguments? I think so::
 
@@ -115,12 +146,13 @@
   
   options:
   
-   -l --listen     ip to listen on (default: localhost)
-   -p --port       port to listen on (default: 8000)
-   -d --daemonize  daemonize process
-      --pid-file   name of file to write process ID to
-   -t --test       testing help for a function (default: test)
-   -h --help       show help
+   -l --listen       ip to listen on (default: localhost)
+   -p --port         port to listen on (default: 8000)
+   -d --daemonize    daemonize process
+      --pid-file     name of file to write process ID to
+   -D --definitions  just some definitions
+   -t --test         testing help for a function (default: test)
+   -h --help         show help
 
 
 That's all for today; see you next time!
--- a/tests/test_opts.py	Wed Jan 05 20:17:20 2011 +0100
+++ b/tests/test_opts.py	Wed Jan 05 20:41:43 2011 +0100
@@ -7,6 +7,7 @@
             port=('p', 8000, 'port to listen on'),
             daemonize=('d', False, 'daemonize process'),
             pid_file=('', '', 'name of file to write process ID to'),
+            definitions=('D', {}, 'just some definitions'),
             test=('t', lambda x: x or 'test', 'testing help for a function')):
     '''Command with option declaration as keyword arguments
     '''