ability to decorate all subcommands and example of such decorator
authorAlexander Solovyov <piranha@piranha.org.ua>
Tue, 04 Aug 2009 20:30:02 +0300
changeset 70 74b690e79606
parent 69 bf6cd5a1fc9e
child 71 78fa11005c83
ability to decorate all subcommands and example of such decorator
finaloption.py
test.py
--- a/finaloption.py	Tue Aug 04 17:04:08 2009 +0300
+++ b/finaloption.py	Tue Aug 04 20:30:02 2009 +0300
@@ -84,14 +84,16 @@
     return wrapper
 
 
-def dispatch(args=None, cmdtable=None, globalopts=None):
+def dispatch(args=None, cmdtable=None, globaloptions=None,
+             middleware=lambda x: x):
     '''Dispatch command arguments based on subcommands.
 
     - ``args``: list of arguments, default: ``sys.argv[1:]``
     - ``cmdtable``: dict of commands in format described below.
       If not supplied, will use functions decorated with ``@command``.
-    - ``globalopts``: list of options which are applied to all
-      commands, if not supplied will contain ``--help`` option
+    - ``globaloptions``: list of options which are applied to all
+      commands, will contain ``--help`` option at least.
+    - ``middleware``: global decorator for all commands.
 
     cmdtable format description::
 
@@ -108,18 +110,18 @@
     args = args or sys.argv[1:]
     cmdtable = cmdtable or CMDTABLE
 
-    globalopts = globalopts or []
-    globalopts.append(('h', 'help', False, 'display help'))
+    globaloptions = globaloptions or []
+    globaloptions.append(('h', 'help', False, 'display help'))
 
-    cmdtable['help'] = (help_(cmdtable, globalopts), [], '[TOPIC]')
+    cmdtable['help'] = (help_(cmdtable, globaloptions), [], '[TOPIC]')
     help_func = cmdtable['help'][0]
 
     try:
         name, func, args, kwargs = catcher(
-            lambda: _dispatch(args, cmdtable, globalopts),
+            lambda: _dispatch(args, cmdtable, globaloptions),
             help_func)
         return catcher(
-            lambda: call_cmd(name, func, *args, **kwargs),
+            lambda: call_cmd(name, middleware(func), *args, **kwargs),
             help_func)
     except Abort:
         pass
@@ -130,7 +132,7 @@
 # --------
 
 def help_(cmdtable, globalopts):
-    def inner(name=None):
+    def help_inner(name=None):
         '''Show help for a given help topic or a help overview
 
         With no arguments, print a list of commands with short help messages.
@@ -175,7 +177,7 @@
         return help_cmd(cmd,
                         replace_name(usage, sysname() + ' ' + aliases[0]),
                         options + globalopts)
-    return inner
+    return help_inner
 
 def help_cmd(func, usage, options):
     '''show help for given command
--- a/test.py	Tue Aug 04 17:04:08 2009 +0300
+++ b/test.py	Tue Aug 04 20:30:02 2009 +0300
@@ -6,18 +6,20 @@
 
 
 @command(usage='[-t]', shortlist=True)
-def simple(test=('t', False, 'just test execution')):
+def simple(ui,
+           test=('t', False, 'just test execution')):
     '''Just simple command to do nothing.
 
     I assure you! Nothing to look here. ;-)
     '''
-    print locals()
+    ui.write(str(locals()))
+    ui.write('\n')
 
 cplx_opts = [('p', 'pass', False, 'don\'t run the command'),
              ('', 'exit', 0, 'exit with supplied code (default: 0)')]
 
 @command(cplx_opts, usage='[-p] [--exit value] ...', name='complex', hide=True)
-def complex_(*args, **opts):
+def complex_(ui, *args, **opts):
     '''That's more complex command indented to do something
 
     Let's try to do that (what?!)
@@ -25,8 +27,59 @@
     if opts.get('pass'):
         return
     # test ui
+    ui.write('preved\n')
     if opts.get('exit'):
         sys.exit(opts['exit'])
 
+def ui_middleware(func):
+    if func.__name__ == 'help_inner':
+        return func
+    def extract_dict(source, *keys):
+        dest = {}
+        for k in keys:
+            dest[k] = source.pop(k, None)
+        return dest
+
+    def inner(*args, **kwargs):
+        ui = UI(**extract_dict(kwargs, 'verbose', 'quiet'))
+        return func(ui, *args, **kwargs)
+    return inner
+
+class UI(object):
+    '''User interface helper.
+
+    Intended to ease handling of quiet/verbose output and more.
+
+    You have three methods to handle program messages output:
+
+      - ``UI.info`` is printed by default, but hidden with quiet option
+      - ``UI.note`` is printed only if output is verbose
+      - ``UI.write`` is printed in any case
+
+    Additionally there is ``UI.warn`` method, which prints to stderr.
+    '''
+
+    options = [('v', 'verbose', False, 'enable additional output'),
+               ('q', 'quiet', False, 'suppress output')]
+
+    def __init__(self, verbose=False, quiet=False):
+        self.verbose = verbose
+        # disabling quiet in favor of verbose is more safe
+        self.quiet = (not verbose and quiet)
+
+    def write(self, *messages):
+        for m in messages:
+            sys.stdout.write(m)
+
+    def warn(self, *messages):
+        for m in messages:
+            sys.stderr.write(m)
+
+    info = lambda self, *m: not self.quiet and self.write(*m)
+    note = lambda self, *m: self.verbose and self.write(*m)
+
+
 if __name__ == '__main__':
-    dispatch()
+    dispatch(globaloptions=UI.options, middleware=ui_middleware)
+
+