ability to use commands as usual functions
authorAlexander Solovyov <piranha@piranha.org.ua>
Fri, 24 Jul 2009 13:16:11 +0300
changeset 50 ae36fb1b69a2
parent 49 a3feac00d151
child 51 2d6db0ae956c
ability to use commands as usual functions
docs/overview.rst
finaloption.py
test_opts.py
--- a/docs/overview.rst	Thu Jul 23 16:52:29 2009 +0300
+++ b/docs/overview.rst	Fri Jul 24 13:16:11 2009 +0300
@@ -55,8 +55,27 @@
 function. This is done to comply with standarts of writing both console
 interfaces and Python application.
 
-To make your application work, just call ``main()`` (it will parse
-``sys.argv``).
+After that you can simply call this function as an entry point to your program::
+
+  if __name__ == '__main__':
+      main()
+
+This will run command line parsing facility, using arguments from
+``sys.argv``. ``%name`` will be replaced with ``sys.argv[0]`` (or prepended to
+usage string if there is no ``%name``), and rest of arguments will be passed to
+command line parser. In case if rest is empty, help will be displayed.
+
+Of course, you can use your function programmatically, supplying list of
+arguments to function::
+
+  main('-l 0.0.0.0 /my/dir'.split())
+
+Or, if you need this, you can call this function as usual::
+
+  main('/my/dir', listen='0.0.0.0')
+
+In this case no type conversion (which is done upon arguments parsing) will be
+performed.
 
 Subcommands
 -----------
--- a/finaloption.py	Thu Jul 23 16:52:29 2009 +0300
+++ b/finaloption.py	Fri Jul 24 13:16:11 2009 +0300
@@ -36,7 +36,11 @@
     '''
     def wrapper(func):
         # copy option list
-        options_ = list(options or guess_options(func))
+        try:
+            options_ = list(options or guess_options(func))
+        except TypeError:
+            # no options supplied and no options present in func
+            options_ = []
 
         name_ = name or func.__name__
         CMDTABLE[(shortlist and '^' or '') + name_] = (
@@ -48,24 +52,33 @@
                 name_ = name_[2:]
             return help_cmd(func, replace_name(usage, name_), options_)
 
-        def inner(args=None):
+        @wraps(func)
+        def inner(*arguments, **kwarguments):
+            # look if we need to add 'help' option
             try:
                 (True for option in reversed(options_)
                  if option[1] == 'help').next()
             except StopIteration:
                 options_.append(('h', 'help', False, 'show help'))
 
-            args = args or sys.argv[1:]
-            if not args:
-                return help_func()
+            args = kwarguments.pop('args', None)
+            if arguments or kwarguments:
+                args, opts = arguments, kwarguments
+            else:
+                args = args or sys.argv[1:]
+                if not args:
+                    return help_func()
+                try:
+                    opts, args = catcher(lambda: parse(args, options_),
+                                         help_func)
+                except Abort:
+                    return -1
 
             try:
-                opts, args = catcher(lambda: parse(args, options_), help_func)
                 if opts.pop('help', False):
                     return help_func()
-                return catcher(
-                    lambda: call_cmd(name_, func, *args, **opts),
-                    help_func)
+                return catcher(lambda: call_cmd(name_, func, *args, **opts),
+                               help_func)
             except Abort:
                 return -1
 
@@ -419,6 +432,19 @@
         return usage.replace('%name', name, 1)
     return name + ' ' + usage
 
+try:
+    from functools import wraps
+except ImportError:
+    def wraps(wrapped, assigned=('__module__', '__name__', '__doc__'),
+              updated=('__dict__',)):
+        def inner(wrapper):
+            for attr in assigned:
+                setattr(wrapper, attr, getattr(wrapped, attr))
+            for attr in updated:
+                getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
+            return wrapper
+        return inner
+
 # --------
 # Exceptions
 # --------
--- a/test_opts.py	Thu Jul 23 16:52:29 2009 +0300
+++ b/test_opts.py	Fri Jul 24 13:16:11 2009 +0300
@@ -25,7 +25,7 @@
             pid_file=('', '', 'name of file to write process ID to')):
     '''Command with option declaration as keyword arguments
 
-    Otherwise it's the same as previons command
+    Otherwise it's the same as previous command
     '''
     print locals()