locale troubles fixed, test suite moved to cram
authorAlexander Solovyov <piranha@piranha.org.ua>
Mon, 01 Nov 2010 17:41:57 +0100
changeset 123 4d73a8af4bbf
parent 121 836bb50a8660
child 127 67a0ead9bb59
locale troubles fixed, test suite moved to cram
README
docs/changelog.rst
opster.py
runtests
tests/multicommands.py
tests/opster.t
tests/runtests
tests/test
tests/test.out
tests/test.py
tests/test2
tests/test_cmd
tests/test_cmd.out
tests/test_cmd.py
tests/test_opts
tests/test_opts.err
tests/test_opts.out
tests/test_opts.py
--- a/README	Sun Sep 19 20:17:49 2010 +0300
+++ b/README	Mon Nov 01 17:41:57 2010 +0100
@@ -4,7 +4,7 @@
  Opster
 ========
 
-Opster is a command line parser, intended to make writing command line
+Opster is a command line options parser, intended to make writing command line
 applications easy and painless. It uses built-in Python types (lists,
 dictionaries, etc) to define options, which makes configuration clear and
 concise. Additionally it contains possibility to handle subcommands (i.e.
--- a/docs/changelog.rst	Sun Sep 19 20:17:49 2010 +0300
+++ b/docs/changelog.rst	Mon Nov 01 17:41:57 2010 +0100
@@ -1,6 +1,11 @@
 Changelog
 ---------
 
+0.9.12
+~~~~~~
+
+ - fixed trouble with non-ascii characters in docstrings
+
 0.9.11
 ~~~~~~
 
--- a/opster.py	Sun Sep 19 20:17:49 2010 +0300
+++ b/opster.py	Mon Nov 01 17:41:57 2010 +0100
@@ -10,8 +10,22 @@
 __author__ = 'Alexander Solovyov'
 __email__ = 'piranha@piranha.org.ua'
 
-write = sys.stdout.write
-err = sys.stderr.write
+try:
+    import locale
+    ENCODING = locale.getpreferredencoding()
+    if not ENCODING or ENCODING == 'mac-roman':
+        ENCODING = 'utf-8'
+except locale.Error:
+    ENCODING = 'utf-8'
+
+def write(text, out=sys.stdout):
+    encoding = locale.getpreferredencoding()
+    if isinstance(text, unicode):
+        return out.write(text.encode(encoding))
+    out.write(text)
+
+def err(text):
+    write(text, out=sys.stderr)
 
 CMDTABLE = {}
 
@@ -219,9 +233,7 @@
     <BLANKLINE>
     '''
     write(usage + '\n')
-    doc = func.__doc__
-    if not doc:
-        doc = '(no help text available)'
+    doc = func.__doc__ or '(no help text available)'
     write('\n' + doc.strip() + '\n\n')
     if options:
         write(''.join(help_options(options)))
@@ -274,10 +286,10 @@
         # might have the fifth completer element
         short, name, default, comment = o[:4]
         if short and len(short) != 1:
-            raise FOError('Short option should be only a single'
-                          ' character: %s' % short)
+            raise OpsterError(
+                'Short option should be only a single character: %s' % short)
         if not name:
-            raise FOError(
+            raise OpsterError(
                 'Long name should be defined for every option')
         # change name to match Python styling
         pyname = name.replace('-', '_')
@@ -449,7 +461,7 @@
     except getopt.GetoptError, e:
         err('error: %s\n' % e)
         help_func()
-    except FOError, e:
+    except OpsterError, e:
         err('%s\n' % e)
 
 def call_cmd(name, func):
@@ -604,5 +616,5 @@
 class ParseError(CommandException):
     'Raised on error in command line parsing'
 
-class FOError(CommandException):
+class OpsterError(CommandException):
     'Raised on trouble with opster configuration'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/runtests	Mon Nov 01 17:41:57 2010 +0100
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+cd tests
+cram -E *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/multicommands.py	Mon Nov 01 17:41:57 2010 +0100
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+
+from opster import dispatch, command
+
+
+@command(usage='[-t]', shortlist=True)
+def simple(ui,
+           test=('t', False, 'just test execution')):
+    '''Just simple command to print keys of received arguments.
+
+    I assure you! Nothing to look here. ;-)
+    '''
+    ui.write(str(locals().keys()))
+    ui.write('\n')
+
+cplx_opts = [('p', 'pass', False, 'don\'t run the command'),
+             ('', 'exit', 0, 'exit with supplied code (default: 0)'),
+             ('n', 'name', '', 'optional name')]
+
+@command(cplx_opts, usage='[-p] [--exit value] ...', name='complex', hide=True)
+def complex_(ui, *args, **opts):
+    u'''That's more complex command indented to do something
+
+    И самое главное - мы тут немножечко текста не в ascii напишем
+    и посмотрим, что будет. :)
+    '''
+    if opts.get('pass'):
+        return
+    # test ui
+    ui.write('write\n')
+    ui.note('note\n')
+    ui.info('info\n')
+    ui.warn('warn\n')
+    if opts.get('exit'):
+        sys.exit(opts['exit'])
+
+def ui_middleware(func):
+    def extract_dict(source, *keys):
+        dest = {}
+        for k in keys:
+            dest[k] = source.pop(k, None)
+        return dest
+
+    def inner(*args, **kwargs):
+        opts = extract_dict(kwargs, 'verbose', 'quiet')
+        if func.__name__ == 'help_inner':
+            return func(*args, **kwargs)
+        ui = UI(**opts)
+        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(globaloptions=UI.options, middleware=ui_middleware)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/opster.t	Mon Nov 01 17:41:57 2010 +0100
@@ -0,0 +1,70 @@
+This is a test suite for opster library. Just read it to get some idea of how it
+works.
+
+Define some help functions::
+
+  $ function run() { python $TESTDIR/$@; }
+
+Check if usage is working::
+
+  $ run multicommands.py
+  usage: /Users/piranha/dev/misc/opster/tests/multicommands.py <command> [options]
+  
+  commands:
+  
+   simple  Just simple command to print keys of received arguments.
+
+Ok, then let's run it::
+
+  $ run multicommands.py simple
+  ['test', 'ui']
+
+Yeah, nice one, but we know that command ``complex`` is just hidden there. Let's
+check it out::
+
+  $ run multicommands.py help complex
+  /Users/piranha/dev/misc/opster/tests/multicommands.py complex [-p] [--exit value] ...
+  
+  That's more complex command indented to do something
+  
+      И самое главное - мы тут немножечко текста не в ascii напишем
+      и посмотрим, что будет. :)
+  
+  options:
+  
+   -p --pass     don't run the command
+      --exit     exit with supplied code (default: 0)
+   -n --name     optional name
+   -v --verbose  enable additional output
+   -q --quiet    suppress output
+   -h --help     display help
+
+Now we're going to test if a script with a single command will work (not
+everyone needs subcommands, you know)::
+
+  $ run test_opts.py
+  another: invalid arguments
+  /Users/piranha/dev/misc/opster/tests/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
+
+Yeah, I've got it, I should supply some argument::
+
+  $ run test_opts.py right-here
+  {'daemonize': False,
+   'dirname': 'right-here',
+   'listen': 'localhost',
+   'pid_file': '',
+   'port': 8000,
+   'test': 'test'}
+
+That's all for today; see you next time!
--- a/tests/runtests	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-BASE=$(dirname $0)/..
-STDOUT=$BASE/tests/tmp.out
-STDERR=$BASE/tests/tmp.err
-
-for f in `ls .`; do
-    if [ -x "$f" -a $f != "runtests" ]; then
-        echo ---------------- $f
-        PYTHONPATH=$BASE $BASE/tests/$f >$STDOUT 2>$STDERR
-        diff -N $BASE/tests/$f.out $STDOUT
-        diff -N $BASE/tests/$f.err $STDERR
-    fi
-done
-
-rm -f $STDOUT
-rm -f $STDERR
--- a/tests/test	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-python test.py
-python test.py simple
-python test.py help complex
--- a/tests/test.out	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-usage: test.py <command> [options]
-
-commands:
-
- simple  Just simple command to do nothing.
-['test', 'ui']
-test.py complex [-p] [--exit value] ...
-
-That's more complex command indented to do something
-
-    Let's try to do that (what?!)
-
-options:
-
- -p --pass     don't run the command
-    --exit     exit with supplied code (default: 0)
- -n --name     optional name
- -v --verbose  enable additional output
- -q --quiet    suppress output
- -h --help     display help
--- a/tests/test.py	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +0,0 @@
-#!/usr/bin/env python
-
-import sys
-
-from opster import dispatch, command
-
-
-@command(usage='[-t]', shortlist=True)
-def simple(ui,
-           test=('t', False, 'just test execution')):
-    '''Just simple command to do nothing.
-
-    I assure you! Nothing to look here. ;-)
-    '''
-    ui.write(str(locals().keys()))
-    ui.write('\n')
-
-cplx_opts = [('p', 'pass', False, 'don\'t run the command'),
-             ('', 'exit', 0, 'exit with supplied code (default: 0)'),
-             ('n', 'name', '', 'optional name')]
-
-@command(cplx_opts, usage='[-p] [--exit value] ...', name='complex', hide=True)
-def complex_(ui, *args, **opts):
-    '''That's more complex command indented to do something
-
-    Let's try to do that (what?!)
-    '''
-    if opts.get('pass'):
-        return
-    # test ui
-    ui.write('write\n')
-    ui.note('note\n')
-    ui.info('info\n')
-    ui.warn('warn\n')
-    if opts.get('exit'):
-        sys.exit(opts['exit'])
-
-def ui_middleware(func):
-    def extract_dict(source, *keys):
-        dest = {}
-        for k in keys:
-            dest[k] = source.pop(k, None)
-        return dest
-
-    def inner(*args, **kwargs):
-        opts = extract_dict(kwargs, 'verbose', 'quiet')
-        if func.__name__ == 'help_inner':
-            return func(*args, **kwargs)
-        ui = UI(**opts)
-        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(globaloptions=UI.options, middleware=ui_middleware)
--- a/tests/test2	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-
-from opster import command
-
-a = 0
-
-@command(usage="%name [-p|--pptx] [-x|--xslx] [-w|--wrdx] [-a|--abstract]")
-def main(pptx=('p', False, 'should we generate pptx related code'),
-         xslx=('x', False, 'should we generate xslx related code'),
-         wrdx=('w', False, 'should we generate wrdx related code'),
-         abstract=('a', False, 'should we generate abstract ifaces')):
-    """
-    Code generation tool. Run without params to regenerate all the code
-    """
-    global a
-    a = 42
-
-if __name__ == '__main__':
-    main()
-    assert a == 42, "WTF???"
--- a/tests/test_cmd	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-python test_cmd.py
-python test_cmd.py help runserver
-python test_cmd.py initdb
--- a/tests/test_cmd.out	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,17 +0,0 @@
-usage: test_cmd.py <command> [options]
-
-commands:
-
- help       Show help for a given help topic or a help overview
- initdb     Initialize database
- runserver  Run development server
-test_cmd.py runserver [OPTIONS] 
-
-Run development server
-
-options:
-
- -l --listen  ip to listen on (default: localhost)
- -p --port    port to listen on (default: 5000)
- -c --config  config file to use (default: webshops.ini)
- -h --help    display help
--- a/tests/test_cmd.py	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +0,0 @@
-#!/usr/bin/env python
-
-import opster
-
-config_opts=[('c', 'config', 'webshops.ini', 'config file to use')]
-
-@opster.command(config_opts)
-def initdb(config):
-    """Initialize database"""
-    pass
-
-@opster.command(config_opts)
-def runserver(listen=('l', 'localhost', 'ip to listen on'),
-              port=('p', 5000, 'port to listen on'),
-              **opts):
-    """Run development server"""
-    print locals()
-
-if __name__ == '__main__':
-    opster.dispatch()
--- a/tests/test_opts	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-#!/bin/sh
-
-python test_opts.py --help
-python test_opts.py
-python test_opts.py .
--- a/tests/test_opts.err	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-another: invalid arguments
--- a/tests/test_opts.out	Sun Sep 19 20:17:49 2010 +0300
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +0,0 @@
-test_opts.py [-l HOST] DIR
-
-Command with option declaration as keyword arguments
-
-    Otherwise it's the same as previous command
-
-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
-test_opts.py [-l HOST] DIR
-
-Command with option declaration as keyword arguments
-
-    Otherwise it's the same as previous command
-
-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
-{'pid_file': '', 'daemonize': False, 'test': 'test', 'dirname': '.', 'port': 8000, 'listen': 'localhost'}
--- a/tests/test_opts.py	Sun Sep 19 20:17:49 2010 +0300
+++ b/tests/test_opts.py	Mon Nov 01 17:41:57 2010 +0100
@@ -1,22 +1,6 @@
-#!/usr/bin/env python
-
-import sys
-
+import pprint
 from opster import command
 
-opts = [('l', 'listen', 'localhost', 'ip to listen on'),
-        ('p', 'port', 8000, 'port to listen on'),
-        ('d', 'daemonize', False, 'daemonize process'),
-        ('', 'pid-file', '', 'name of file to write process ID to')]
-
-@command(opts, usage='[-l HOST] DIR')
-def main(*dirs, **opts):
-    '''This is some command
-
-    It looks very similar to some serve command
-    '''
-    print locals()
-
 @command(usage='[-l HOST] DIR')
 def another(dirname,
             listen=('l', 'localhost', 'ip to listen on'),
@@ -25,11 +9,8 @@
             pid_file=('', '', 'name of file to write process ID to'),
             test=('t', lambda x: x or 'test', 'testing help for a function')):
     '''Command with option declaration as keyword arguments
-
-    Otherwise it's the same as previous command
     '''
-    print locals()
+    pprint.pprint(locals())
 
 if __name__ == '__main__':
-    #main()
     another()