--- 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()