# HG changeset patch
# User Alexander Solovyov <piranha@piranha.org.ua>
# Date 1252243952 -10800
# Node ID ec9736668bcafa1da3724b57a719427366f124ef
# Parent  10399ada69e6c45b71aa97e33b630a788ccb56b0
when calling command from python set not supplied options to proper defaults

Earlier you would get full option spec (short, default, help) for every option,
not supplied as argument (or keyword argument) in function call.

diff -r 10399ada69e6 -r ec9736668bca docs/changelog.rst
--- a/docs/changelog.rst	Sun Sep 06 15:08:49 2009 +0300
+++ b/docs/changelog.rst	Sun Sep 06 16:32:32 2009 +0300
@@ -3,9 +3,11 @@
 
 0.9.9
 ~~~~~
- - globaloptions were simply dropped after parsing, fold them in regular options
- - replace _ with - in command names, same as in options names
- - respect empty strings as usage
+ - Now it's possible to call commands as regular function, where every
+   non-supplied option will receive proper default (defined in option spec)
+ - Globaloptions were simply dropped after parsing, fold them in regular options
+ - Replace _ with - in command names, same as in options names
+ - Respect empty strings as usage
 
 0.9.8
 ~~~~~
diff -r 10399ada69e6 -r ec9736668bca opster.py
--- a/opster.py	Sun Sep 06 15:08:49 2009 +0300
+++ b/opster.py	Sun Sep 06 16:32:32 2009 +0300
@@ -56,7 +56,7 @@
             return help_cmd(func, replace_name(usage_, sysname()), options_)
 
         @wraps(func)
-        def inner(*arguments, **kwarguments):
+        def inner(*args, **opts):
             # look if we need to add 'help' option
             try:
                 (True for option in reversed(options_)
@@ -64,16 +64,21 @@
             except StopIteration:
                 options_.append(('h', 'help', False, 'show help'))
 
-            args = kwarguments.pop('args', None)
-            if arguments or kwarguments:
-                args, opts = arguments, kwarguments
-            else:
-                args = args or sys.argv[1:]
-                try:
-                    opts, args = catcher(lambda: parse(args, options_),
-                                         help_func)
-                except Abort:
-                    return -1
+            argv = opts.pop('args', None)
+            if opts.pop('help', False):
+                return help_func()
+
+            if args or opts:
+                # no catcher here because this is call from Python
+                return call_cmd_regular(func)(*args, **opts)
+
+            if argv is None:
+                argv = sys.argv[1:]
+
+            try:
+                opts, args = catcher(lambda: parse(argv, options_), help_func)
+            except Abort:
+                return -1
 
             try:
                 if opts.pop('help', False):
@@ -416,6 +421,8 @@
     return usage
 
 def catcher(target, help_func):
+    '''Catches all exceptions and prints human-readable information on them
+    '''
     try:
         return target()
     except UnknownCommand, e:
@@ -451,6 +458,18 @@
             raise
     return inner
 
+def call_cmd_regular(func):
+    def inner(*args, **kwargs):
+        funcargs, varargs, varkw, defaults = inspect.getargspec(func)
+
+        funckwargs = funcargs[len(args):]
+        funckwargs = dict(zip(funckwargs, (default for _, default, _
+                                           in defaults[-len(funckwargs):])))
+        funckwargs.update(kwargs)
+
+        return func(*args, **funckwargs)
+    return inner
+
 def replace_name(usage, name):
     if '%name' in usage:
         return usage.replace('%name', name, 1)
diff -r 10399ada69e6 -r ec9736668bca test_cmd.py
--- a/test_cmd.py	Sun Sep 06 15:08:49 2009 +0300
+++ b/test_cmd.py	Sun Sep 06 16:32:32 2009 +0300
@@ -21,4 +21,5 @@
     print opts
 
 
-opster.dispatch()
+if __name__ == '__main__':
+    opster.dispatch()