commando-0.3.4/ 0000755 0000765 0000024 00000000000 12151441026 014765 5 ustar lakshmivyas staff 0000000 0000000 commando-0.3.4/AUTHORS.rst 0000644 0000765 0000024 00000001600 12133161710 016640 0 ustar lakshmivyas staff 0000000 0000000 -------------------------
Authors
-------------------------
* `Lakshmi Vyas`_
- Maintainer.
* `Brandon Philips`_
- Clean up packaging issues.
* Use ``distribute`` to peek into the package to get the issue number.
{38e21c48680873fc7409effb654c0c733024e9e1}.
* Get ``install_requires`` from the ``requirements.txt`` file.
{d8b29fa3fe2f0bdeeda91c445a9aea38b8fa5b70}.
* `Ben West`_
- Allow parameterless subcommands and commands. (Pull #4).
* `Julien Danjou`_
- Support for nested subcommands. (Pull #7)
* `fruch`_
Use the parameters in the same order as provided. (Pulls #8)
.. _Lakshmi Vyas: https://github.com/lakshmivyas
.. _Brandon Philips: https://github.com/philips
.. _Ben West: https://github.com/bewest
.. _Julien Danjou: https://github.com/jd
.. _fruch: https://github.com/fruch
commando-0.3.4/CHANGELOG.rst 0000644 0000765 0000024 00000003472 12151440605 017016 0 ustar lakshmivyas staff 0000000 0000000 Version 0.3.4
--------------
- Use proper error code on exit.
Version 0.3.3a
--------------
- Fix exception handling.
- Never use `__parser__.error`
- Always log errors and traceback
- Add wrapper methods for `__parser__`.
- `exit`, `error`
- `print_usage, `print_help`
- `format_usage, `format_help`
- Improve docstrings.
Version 0.3.2a
--------------
- Add `load_python_object` to load a python object using a qualified name.
Version 0.3.1a
--------------
Thanks to `fruch_ :
- Preserve the order of parameters in declaration.
Version 0.3a
--------------
Thanks to `Julien Danjou`_ :
- Add support for nested sub commands.
{0e26a6fe2571accb78d26318ab1b8dc65636d2b0}. (Pull #7)
Version 0.2.1a
--------------
Thanks to `Ben West`_ :
- Allow commands to have no params.
Version 0.2a
--------------
- Bundle various frequently used utilities with commando.
(``ShellCommand``, ``ConfigDict``, ``autoprop`` and logging helpers).
{63525646bb366f4def3c5065a51a404b18269873}. (Pull #4)
Version 0.1.3a
--------------
- Commando must consume exceptions by default. Any exception should be
communicated in a friendly manner to the user via the parser or the
given logger. {0e26a6fe2571accb78d26318ab1b8dc65636d2b0}.
Version 0.1.2a
---------------
Thanks to `Brandon Philips`_ :
- Use ``distribute_setup.py``.
- Derive version from ``pkg_resources``.
- Add ``argparse`` as a dependency.
Version 0.1.1a
---------------
- Add more decorators that map to argparse parameters.
Version 0.1a
------------
- Create a simple meta programmed wrapper around ``argparse``.
.. _Lakshmi Vyas: https://github.com/lakshmivyas
.. _Brandon Philips: https://github.com/philips
.. _Ben West: https://github.com/bewest
.. _Julien Danjou: https://github.com/jd
.. _fruch: https://github.com/fruch
commando-0.3.4/commando/ 0000755 0000765 0000024 00000000000 12151441026 016562 5 ustar lakshmivyas staff 0000000 0000000 commando-0.3.4/commando/__init__.py 0000644 0000765 0000024 00000000105 12151422232 020665 0 ustar lakshmivyas staff 0000000 0000000 # pylint: disable-msg=C0111,W0401
from commando.application import *
commando-0.3.4/commando/application.py 0000644 0000765 0000024 00000021326 12151440411 021440 0 ustar lakshmivyas staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Declarative interface for argparse
"""
from argparse import ArgumentParser
from collections import namedtuple
from commando.util import getLoggerWithConsoleHandler
import logging
import sys
# pylint: disable-msg=R0903,C0103,C0301
try:
import pkg_resources
# pylint: disable-msg=E1103
__version__ = pkg_resources.get_distribution('commando').version
except Exception: # pylint: disable-msg=W0703
__version__ = 'unknown'
__all__ = [
'__version__',
'command',
'subcommand',
'param',
'version',
'store',
'true',
'false',
'append',
'const',
'append_const',
'Application'
]
class Commando(type):
"""
Meta class that enables declarative command definitions
"""
# pylint: disable-msg=R0912
def __new__(mcs, name, bases, attrs):
instance = super(Commando, mcs).__new__(mcs, name, bases, attrs)
subcommands = []
main_command = None
main_parser = None
# pylint: disable-msg=C0111
# Collect commands based on decorators
for member in attrs.itervalues():
if hasattr(member, "command"):
main_command = member
elif hasattr(member, "subcommand"):
subcommands.append(member)
def add_arguments(func):
params = getattr(func, 'params', [])
for parameter in reversed(params):
func.parser.add_argument(*parameter.args, **parameter.kwargs)
def add_subparser(func):
if getattr(func, 'parser', None):
# Already initialized
return
if not func.parent:
# Sub of main
func.parent = main_command
else:
# Sub of something else
if not getattr(func.parent, 'parser', None):
# Parser doesn't exist for the parent.
add_subparser(func.parent)
if not getattr(func.parent, 'subparsers', None):
# Subparser collection doesn't exist for the parent.
func.parent.subparsers = func.parent.parser.add_subparsers()
func.parser = func.parent.subparsers.add_parser(
*func.subcommand.args,
**func.subcommand.kwargs)
add_arguments(func)
if main_command:
main_parser = ArgumentParser(*main_command.command.args,
**main_command.command.kwargs)
main_command.parser = main_parser
add_arguments(main_command)
if len(subcommands):
main_command.subparsers = main_parser.add_subparsers()
for sub in subcommands:
add_subparser(sub)
for sub in subcommands:
# Map the functions to the subparser actions
if not getattr(sub, 'subparsers', None):
# Only if there are no subcommands
sub.parser.set_defaults(run=sub)
instance.__main__ = main_command
instance.__parser__ = main_parser
return instance
values = namedtuple('__meta_values', 'args, kwargs')
class metarator(object):
"""
A generic decorator that tags the decorated method with
the passed in arguments for meta classes to process them.
"""
def __init__(self, *args, **kwargs):
self.values = values._make((args, kwargs)) # pylint: disable-msg=W0212
def metarate(self, func, name='values'):
"""
Set the values object to the function object's namespace
"""
setattr(func, name, self.values)
return func
def __call__(self, func):
return self.metarate(func)
class command(metarator):
"""
Used to decorate the main entry point
"""
def __call__(self, func):
return self.metarate(func, name='command')
class subcommand(metarator):
"""
Used to decorate the subcommands
"""
def __init__(self, *args, **kwargs):
self.parent = kwargs.get('parent')
try:
del kwargs['parent']
except KeyError:
pass
super(subcommand, self).__init__(*args, **kwargs)
def __call__(self, func):
func.parent = self.parent
return self.metarate(func, name='subcommand')
class param(metarator):
"""
Use this decorator instead of `ArgumentParser.add_argument`.
"""
def __call__(self, func):
func.params = func.params if hasattr(func, 'params') else []
func.params.append(self.values)
return func
class version(param):
"""
Use this decorator for adding the version argument.
"""
def __init__(self, *args, **kwargs):
super(version, self).__init__(*args, action='version', **kwargs)
class store(param):
"""
Use this decorator for adding the simple params that store data.
"""
def __init__(self, *args, **kwargs):
super(store, self).__init__(*args, action='store', **kwargs)
class true(param):
"""
Use this decorator as a substitute for 'store_true' action.
"""
def __init__(self, *args, **kwargs):
super(true, self).__init__(*args, action='store_true', **kwargs)
class false(param):
"""
Use this decorator as a substitute for 'store_false' action.
"""
def __init__(self, *args, **kwargs):
super(false, self).__init__(*args, action='store_false', **kwargs)
class const(param):
"""
Use this decorator as a substitute for 'store_const' action.
"""
def __init__(self, *args, **kwargs):
super(const, self).__init__(*args, action='store_const', **kwargs)
class append(param):
"""
Use this decorator as a substitute for 'append' action.
"""
def __init__(self, *args, **kwargs):
super(append, self).__init__(*args, action='append', **kwargs)
class append_const(param):
"""
Use this decorator as a substitute for 'append_const' action.
"""
def __init__(self, *args, **kwargs):
super(append_const, self).__init__(*args,
action='append_const',
**kwargs)
class Application(object):
"""
Barebones base class for command line applications.
"""
__metaclass__ = Commando
def __init__(self, raise_exceptions=False, logger=None):
self.raise_exceptions = raise_exceptions
self.logger = logger or getLoggerWithConsoleHandler()
def parse(self, argv):
"""
Delegates to `ArgumentParser.parse_args`
"""
return self.__parser__.parse_args(argv) # pylint: disable-msg=E1101
def exit(self, status=0, message=None):
"""
Delegates to `ArgumentParser.exit`
"""
if status:
self.logger.error(message)
if self.__parser__: # pylint: disable-msg=E1101
self.__parser__.exit(status, message) # pylint: disable-msg=E1101
else:
sys.exit(status)
def error(self, message=None):
"""
Delegates to `ArgumentParser.error`
"""
if self.__parser__: # pylint: disable-msg=E1101
self.__parser__.error(message) # pylint: disable-msg=E1101
else:
self.logger.error(message)
sys.exit(2)
def print_usage(self, out_file=None):
"""
Delegates to `ArgumentParser.print_usage`
"""
return self.__parser__.print_usage(out_file) # pylint: disable-msg=E1101
def print_help(self, out_file=None):
"""
Delegates to `ArgumentParser.print_help`
"""
return self.__parser__.print_help(out_file) # pylint: disable-msg=E1101
def format_usage(self):
"""
Delegates to `ArgumentParser.format_usage`
"""
return self.__parser__.format_usage() # pylint: disable-msg=E1101
def format_help(self):
"""
Delegates to `ArgumentParser.format_help`
"""
return self.__parser__.format_help() # pylint: disable-msg=E1101
def run(self, args=None):
"""
Runs the main command or sub command based on user input
"""
if not args:
args = self.parse(sys.argv[1:])
if getattr(args, 'verbose', False):
self.logger.setLevel(logging.DEBUG)
try:
if hasattr(args, 'run'):
args.run(self, args)
else:
self.__main__(args) # pylint: disable-msg=E1101
except Exception, e: # pylint: disable-msg=W0703
import traceback
self.logger.debug(traceback.format_exc())
self.logger.error(e.message)
if self.raise_exceptions:
raise
sys.exit(2) commando-0.3.4/commando/conf.py 0000644 0000765 0000024 00000007625 12151425234 020076 0 ustar lakshmivyas staff 0000000 0000000 """
A few wrappers and utilities for handling complex
configuration objects.
"""
from collections import defaultdict
SEQS = (tuple, list, set, frozenset)
class ConfigDict(defaultdict):
"""
A patchable dictionary like object that allows accessing items
as attributes.
"""
def __init__(self, initial=None):
super(ConfigDict, self).__init__(ConfigDict)
initial = initial or {}
for key, value in initial.iteritems():
self.__setitem__(key, value)
def __setitem__(self, key, value):
# pylint: disable-msg=C0111
def transform(primitive):
if isinstance(primitive, dict):
return ConfigDict(primitive)
elif isinstance(primitive, SEQS):
seq = type(primitive)
return seq(transform(v) for v in primitive)
else:
return primitive
super(ConfigDict, self).__setitem__(key, transform(value))
def __getitem__(self, key):
return super(ConfigDict, self).__getitem__(key)
def copy(self):
"""
Returns a copy of the config dict object.
"""
return ConfigDict(self)
def patch(self, overrides):
"""
Patches the config with the given overrides.
Example:
If the current dictionary looks like this:
a: 1,
b: {
c: 3,
d: 4
}
and `patch` is called with the following overrides:
b: {
d: 2,
e: 4
},
c: 5
then, the following will be the resulting dictionary:
a: 1,
b: {
c: 3,
d: 2,
e: 4
},
c: 5
"""
overrides = overrides or {}
for key, value in overrides.iteritems():
current = self.get(key)
if isinstance(value, dict) and isinstance(current, dict):
current.patch(value)
else:
self[key] = value
__setattr__ = __setitem__
__getattr__ = __getitem__
# pylint: disable-msg=R0903
class AutoPropDescriptor(object):
"""
Descriptor for providing default values.
"""
def __init__(self, default_prop):
self.default_prop = default_prop
self.name = default_prop.__name__
self.assigned = '_' + self.name
def __get_assigned__(self, instance):
return getattr(instance, self.assigned, None)
def __set_assigned__(self, instance, value):
return setattr(instance, self.assigned, value)
def __get__(self, instance, owner):
value = self.__get_assigned__(instance)
return value or self.default_prop(instance)
def __set__(self, instance, value):
self.__set_assigned__(instance, value)
# pylint: disable-msg=R0903
class AutoPropMetaClass(type):
"""
Meta class for enabling autoprops.
"""
def __new__(mcs, cname, cbases, cattrs):
autoprops = {name: member
for name, member in cattrs.iteritems()
if getattr(member, 'autoprop', False)}
for name, member in autoprops.iteritems():
cattrs[name] = AutoPropDescriptor(member)
return super(AutoPropMetaClass, mcs).__new__(
mcs, cname, cbases, cattrs)
# pylint: disable-msg=R0903
class AutoProp(object):
"""
The base class for all objects supporting autoprops.
Usage:
class Project(AutoProp):
def __init__(self, config=None):
self.config = config or {}
@AutoProp.default
def source_dir(self):
return self.config.get('source')
p = Project({'source': 'test'})
p.source_dir
>>> 'test'
p.source_dir = 'xyz'
p.source_dir
>>> 'xyz'
"""
__metaclass__ = AutoPropMetaClass
@staticmethod
def default(function):
"""
Decorator for autoprops.
"""
function.autoprop = True
return function
commando-0.3.4/commando/tests/ 0000755 0000765 0000024 00000000000 12151441026 017724 5 ustar lakshmivyas staff 0000000 0000000 commando-0.3.4/commando/tests/__init__.py 0000644 0000765 0000024 00000000000 12133161710 022022 0 ustar lakshmivyas staff 0000000 0000000 commando-0.3.4/commando/tests/dev-req.txt 0000644 0000765 0000024 00000000027 12133161710 022026 0 ustar lakshmivyas staff 0000000 0000000 mock==1.0.1
nose==1.2.1 commando-0.3.4/commando/tests/test_commando.py 0000644 0000765 0000024 00000020760 12133161710 023136 0 ustar lakshmivyas staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Use nose
`$ pip install nose`
`$ pip install mock`
`$ nosetests test_commando.py -d -v`
"""
from contextlib import nested
from commando import *
from mock import patch
try:
import cStringIO
StringIO = cStringIO
except ImportError:
import StringIO as xStringIO
StringIO = xStringIO
def trap_exit_fail(f):
def test_wrapper(*args):
try:
f(*args)
except SystemExit:
import traceback
print traceback.format_exc()
assert False
test_wrapper.__name__ = f.__name__
return test_wrapper
def trap_exit_pass(f):
def test_wrapper(*args):
try:
print f.__name__
f(*args)
except SystemExit:
pass
test_wrapper.__name__ = f.__name__
return test_wrapper
class BasicCommandLine(Application):
@command(description='test', prog='Basic')
@param('--force', action='store_true', dest='force1')
@param('--force2', action='store', dest='force2')
@param('--version', action='version', version='%(prog)s 1.0')
def main(self, params):
assert params.force1 == eval(params.force2)
self._main()
def _main(self):
pass
@trap_exit_fail
def test_command_basic():
with patch.object(BasicCommandLine, '_main') as _main:
c = BasicCommandLine()
args = c.parse(['--force', '--force2', 'True'])
c.run(args)
assert _main.call_count == 1
args = c.parse(['--force2', 'False'])
c.run(args)
assert _main.call_count == 2
def test_command_version_param():
with patch.object(BasicCommandLine, '_main') as _main:
c = BasicCommandLine()
exception = False
try:
c.parse(['--version'])
assert False
except SystemExit:
exception = True
assert exception
assert not _main.called
def test_positional_params():
class PositionalCommandLine(Application):
@command(description='test', prog='Basic')
@param('force1', action='store')
@param('force3', action='store')
@param('force2', action='store')
def main(self, params):
self._main(params)
def _main(self, params):
assert params.force1 == '1'
assert params.force3 == '2'
assert params.force2 == '3'
p = PositionalCommandLine()
args = p.parse(['1', '2', '3'])
p.run(args)
def test_command_version():
class VersionCommandLine(Application):
@command(description='test', prog='Basic')
@param('--force', action='store_true', dest='force1')
@param('--force2', action='store', dest='force2')
@version('--version', version='%(prog)s 1.0')
def main(self, params):
assert params.force1 == eval(params.force2)
self._main()
def _main(self):
pass
with patch.object(VersionCommandLine, '_main') as _main:
c = VersionCommandLine()
exception = False
try:
c.parse(['--version'])
assert False
except SystemExit:
exception = True
assert exception
assert not _main.called
class SuperDecoratedCommandLine(Application):
@command(description='test', prog='Basic')
@true('--force', dest='force1')
@store('--force2', dest='force2')
@const('--jam', const='jam')
@version('--version', version='%(prog)s 1.0')
def main(self, params):
assert params.force1 == eval(params.force2)
self._main()
def _main(self):
pass
def test_command_super():
with patch.object(SuperDecoratedCommandLine, '_main') as _main:
c = SuperDecoratedCommandLine()
args = c.parse(['--force', '--force2', 'True'])
c.run(args)
assert _main.call_count == 1
assert not args.jam
args = c.parse(['--force2', 'False', '--jam'])
c.run(args)
assert _main.call_count == 2
assert args.jam == 'jam'
class ComplexCommandLine(Application):
@command(description='test', prog='Complex')
@param('--force', action='store_true', dest='force1')
@param('--force2', action='store', dest='force2')
@param('--version', action='version', version='%(prog)s 1.0')
def main(self, params):
assert params.force1 == eval(params.force2)
self._main()
@subcommand('sub', description='test')
@param('--launch', action='store_true', dest='launch1')
@param('--launch2', action='store', dest='launch2')
def sub(self, params):
assert params.launch1 == eval(params.launch2)
self._sub()
def _main():
pass
def _sub():
pass
@trap_exit_pass
def test_command_subcommands_usage():
with nested(patch.object(ComplexCommandLine, '_main'),
patch.object(ComplexCommandLine, '_sub')) as (_main, _sub):
c = ComplexCommandLine()
c.parse(['--usage'])
@trap_exit_fail
def test_command_subcommands():
with nested(patch.object(ComplexCommandLine, '_main'),
patch.object(ComplexCommandLine, '_sub')) as (_main, _sub):
c = ComplexCommandLine()
args = c.parse(['sub', '--launch', '--launch2', 'True'])
c.run(args)
assert not _main.called
assert _sub.call_count == 1
class EmptyCommandLine(Application):
@command(description='test', prog='Empty')
def main(self, params):
assert params == []
self._main()
@subcommand('sub', description='test sub')
def sub(self, params):
assert params
assert len(params.__dict__) == 1
self._sub()
def _main():
pass
def _sub():
pass
@trap_exit_pass
def test_empty_main_command():
with nested(patch.object(EmptyCommandLine, '_main'),
patch.object(EmptyCommandLine, '_sub')) as (_main, _sub):
e = EmptyCommandLine(raise_exceptions=True)
args = e.parse(None)
e.run(args)
assert not _sub.called
assert _main.call_count == 1
def test_empty_sub_command():
with nested(patch.object(EmptyCommandLine, '_main'),
patch.object(EmptyCommandLine, '_sub')) as (_main, _sub):
e = EmptyCommandLine(raise_exceptions=True)
e.run(e.parse(['sub']))
assert not _main.called
assert _sub.call_count == 1
class NestedCommandLine(Application):
@command(description='test', prog='Nested')
@param('--force', action='store_true', dest='force1')
@param('--force2', action='store', dest='force2')
@param('--version', action='version', version='%(prog)s 1.0')
def main(self, params):
assert params.force1 == eval(params.force2)
self._main()
@subcommand('sub', description='sub')
@param('--launch', action='store_true', dest='launch1')
@param('--launch2', action='store', dest='launch2')
def sub(self, params):
assert params.launch1 == eval(params.launch2)
self._sub()
@subcommand('foobar', description="foo bar!")
def foobar(self, params):
assert False
@subcommand('bla', parent=foobar)
@param('--launch2', action='store_true', dest='launch2')
def foobar_bla(self, params):
assert params.launch2
self._foobar_bla()
@subcommand('blip', parent=foobar)
def foobar_blip(self, params):
assert False
@subcommand('blop', parent=foobar_blip)
def foobar_blip_blop(self, params):
self._foobar_blip_blop()
def _main():
pass
def _sub():
pass
def _foobar_bla():
pass
def _foobar_blip_blop():
pass
@trap_exit_fail
def test_nested_command_subcommands():
with nested(patch.object(NestedCommandLine, '_main'),
patch.object(NestedCommandLine, '_sub'),
patch.object(NestedCommandLine, '_foobar_bla'),
patch.object(NestedCommandLine, '_foobar_blip_blop')) \
as (_main, _sub, _foobar_bla, _foobar_blip_blop):
c = NestedCommandLine(raise_exceptions=True)
args = c.parse(['sub', '--launch', '--launch2', 'True'])
c.run(args)
assert not _main.called
assert _sub.call_count == 1
args = c.parse(['foobar', 'bla', '--launch2'])
c.run(args)
assert not _main.called
assert _sub.call_count == 1
assert _foobar_bla.call_count == 1
args = c.parse(['foobar', 'blip', 'blop'])
c.run(args)
assert not _main.called
assert _sub.call_count == 1
assert _foobar_bla.call_count == 1
assert _foobar_blip_blop.call_count == 1
commando-0.3.4/commando/tests/test_conf.py 0000644 0000765 0000024 00000003704 12133161710 022265 0 ustar lakshmivyas staff 0000000 0000000 from commando.conf import AutoProp, ConfigDict
class TestClass(AutoProp):
@AutoProp.default
def source(self):
return 'source'
def test_auto():
t = TestClass()
assert t.source == 'source'
def test_override():
t = TestClass()
t.source = 'source1'
assert t.source == 'source1'
t.source = 'source2'
assert t.source == 'source2'
t.source = None
assert t.source == 'source'
def test_init():
c = ConfigDict({"a": 1})
assert c.a == 1
assert c["a"] == 1
def test_change():
c = ConfigDict({"a": 1})
assert c.a == 1
c.a = 2
assert c["a"] == 2
def test_two_levels():
c = ConfigDict({"a": 1, "b": {"c": 3}})
assert c.b.c == 3
def test_two_levels_assignment():
c = ConfigDict({"a": 1, "b": {"c": 3}})
d = {"d": 5}
c.b = d
assert c.b.d == 5
assert c.b == d
def test_two_levels_patch():
c = ConfigDict({"a": 1, "b": {"c": 3}})
d = {"d": 5}
c.b.d = d
assert c.b.c == 3
assert c.b.d == d
def test_copy():
c = ConfigDict({"a": 1, "b": {"c": 3}})
d = c.copy()
assert c == d
c.b.c = 4
assert c != d
def test_list():
c = ConfigDict({"a": 1, "b": {"c": 3}})
c.d = [dict(e=1), dict(f=2)]
assert c.d[0].e == 1
assert c.d[1].f == 2
def test_operator():
c = ConfigDict({"a": 1, "b": {"c": 3}})
from operator import attrgetter
assert attrgetter('b.c')(c) == 3
def test_patch_simple():
c = ConfigDict({"a": 1, "b": {"c": 3, "e": 4}})
d = {"b": {"e": 5}}
c.patch(d)
assert c.b.c == 3
assert c.b.e == 5
def test_patch_complex():
c = ConfigDict({
"a": 1,
"b": {"x": 3, "y": 4},
"c": {"x": 5, "y": 6},
"d": {"x": 7, "y": 8}
})
d = {"a": 2, "b": {"z": 5}, "c": [1, 2], "d": {"y": 9}}
c.patch(d)
assert c.a == 2
assert c.b.x == 3
assert c.b.y == 4
assert c.b.z == 5
assert c.c == [1, 2]
assert c.d.x == 7
assert c.d.y == 9
commando-0.3.4/commando/tests/test_loader.py 0000644 0000765 0000024 00000002035 12133161710 022602 0 ustar lakshmivyas staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Use nose
`$ pip install nose`
`$ nosetests`
"""
from commando.util import CommandoLoaderException, load_python_object
from nose.tools import raises
import os
from fswrap import File
def test_can_load_locals():
file_class = load_python_object('fswrap.File')
assert file_class
f = file_class(__file__)
assert f
assert f.name == os.path.basename(__file__)
def test_can_load_from_python_path():
markdown = load_python_object('markdown.markdown')
assert markdown
assert "
h3
" == markdown("### h3")
def test_can_load_module_without_dot():
yaml = load_python_object('yaml')
abc = yaml.load("""
d: efg
l: mno
""")
assert abc['d'] == 'efg'
assert abc['l'] == 'mno'
@raises(CommandoLoaderException)
def test_exception_raised_for_invalid_module():
load_python_object("junk.junk.junk")
assert False
@raises(CommandoLoaderException)
def test_exception_raised_for_invalid_object():
load_python_object("markdown.junk")
assert False commando-0.3.4/commando/util.py 0000644 0000765 0000024 00000013112 12151425405 020112 0 ustar lakshmivyas staff 0000000 0000000 """
Logging and other aspects.
"""
# pylint: disable-msg=C0103
import logging
import sys
from logging import NullHandler
from subprocess import check_call, check_output, Popen
class CommandoLoaderException(Exception):
"""
Exception raised when `load_python_object` fails.
"""
pass
def load_python_object(name):
"""
Loads a python module from string
"""
logger = getLoggerWithNullHandler('commando.load_python_object')
(module_name, _, object_name) = name.rpartition(".")
if module_name == '':
(module_name, object_name) = (object_name, module_name)
try:
logger.debug('Loading module [%s]' % module_name)
module = __import__(module_name)
except ImportError:
raise CommandoLoaderException(
"Module [%s] cannot be loaded." % module_name)
if object_name == '':
return module
try:
module = sys.modules[module_name]
except KeyError:
raise CommandoLoaderException(
"Error occured when loading module [%s]" % module_name)
try:
logger.debug('Getting object [%s] from module [%s]' %
(object_name, module_name))
return getattr(module, object_name)
except AttributeError:
raise CommandoLoaderException(
"Cannot load object [%s]. "
"Module [%s] does not contain object [%s]. "
"Please fix the configuration or "
"ensure that the module is installed properly" %
(
name,
module_name,
object_name
)
)
class ShellCommand(object):
"""
Provides a simpler interface for calling shell commands.
Wraps `subprocess`.
"""
def __init__(self, cwd=None, cmd=None):
self.cwd = cwd
self.cmd = cmd
def __process__(self, *args, **kwargs):
if self.cmd and not kwargs.get('shell', False):
new_args = [self.cmd]
new_args.extend(args)
args = new_args
args = [arg for arg in args if arg]
if self.cwd and 'cwd' not in kwargs:
kwargs['cwd'] = self.cwd
return (args, kwargs)
def call(self, *args, **kwargs):
"""
Delegates to `subprocess.check_call`.
"""
args, kwargs = self.__process__(*args, **kwargs)
return check_call(args, **kwargs)
def get(self, *args, **kwargs):
"""
Delegates to `subprocess.check_output`.
"""
args, kwargs = self.__process__(*args, **kwargs)
return check_output(args, **kwargs)
def open(self, *args, **kwargs):
"""
Delegates to `subprocess.Popen`.
"""
args, kwargs = self.__process__(*args, **kwargs)
return Popen(args, **kwargs)
def getLoggerWithConsoleHandler(logger_name=None):
"""
Gets a logger object with a pre-initialized console handler.
"""
logger = logging.getLogger(logger_name)
logger.setLevel(logging.INFO)
if not logger.handlers:
handler = logging.StreamHandler(sys.stdout)
if sys.platform == 'win32':
formatter = logging.Formatter(
fmt="%(asctime)s %(name)s %(message)s",
datefmt='%H:%M:%S')
else:
formatter = ColorFormatter(fmt="$RESET %(asctime)s "
"$BOLD$COLOR%(name)s$RESET "
"%(message)s", datefmt='%H:%M:%S')
handler.setFormatter(formatter)
logger.addHandler(handler)
return logger
def getLoggerWithNullHandler(logger_name):
"""
Gets the logger initialized with the `logger_name`
and a NullHandler.
"""
logger = logging.getLogger(logger_name)
if not logger.handlers:
logger.addHandler(NullHandler())
return logger
## Code stolen from :
## http://stackoverflow.com/q/384076
##
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
COLORS = {
'WARNING': YELLOW,
'INFO': WHITE,
'DEBUG': BLUE,
'CRITICAL': YELLOW,
'ERROR': RED,
'RED': RED,
'GREEN': GREEN,
'YELLOW': YELLOW,
'BLUE': BLUE,
'MAGENTA': MAGENTA,
'CYAN': CYAN,
'WHITE': WHITE,
}
RESET_SEQ = "\033[0m" # pylint: disable-msg=W1401
COLOR_SEQ = "\033[1;%dm" # pylint: disable-msg=W1401
BOLD_SEQ = "\033[1m" # pylint: disable-msg=W1401
class ColorFormatter(logging.Formatter):
"""
Basic formatter to show colorful log lines.
"""
def __init__(self, *args, **kwargs):
# can't do super(...) here because Formatter is an old school class
logging.Formatter.__init__(self, *args, **kwargs)
def format(self, record):
levelname = record.levelname
color = COLOR_SEQ % (30 + COLORS[levelname])
message = logging.Formatter.format(self, record)
message = message.replace("$RESET", RESET_SEQ)\
.replace("$BOLD", BOLD_SEQ)\
.replace("$COLOR", color)
for key, value in COLORS.items():
message = message.replace("$" + key, COLOR_SEQ % (value + 30))\
.replace("$BG" + key, COLOR_SEQ % (value + 40))\
.replace("$BG-" + key, COLOR_SEQ % (value + 40))
return message + RESET_SEQ
logging.ColorFormatter = ColorFormatter
__all__ = [
'CommandoLoaderException',
'load_python_object',
'ShellCommand',
'ColorFormatter',
'getLoggerWithNullHandler',
'getLoggerWithConsoleHandler'
] commando-0.3.4/commando.egg-info/ 0000755 0000765 0000024 00000000000 12151441026 020254 5 ustar lakshmivyas staff 0000000 0000000 commando-0.3.4/commando.egg-info/dependency_links.txt 0000644 0000765 0000024 00000000001 12151441026 024322 0 ustar lakshmivyas staff 0000000 0000000
commando-0.3.4/commando.egg-info/PKG-INFO 0000644 0000765 0000024 00000010100 12151441026 021341 0 ustar lakshmivyas staff 0000000 0000000 Metadata-Version: 1.1
Name: commando
Version: 0.3.4
Summary: A declarative interface to argparse with additional utilities
Home-page: http://github.com/lakshmivyas/commando
Author: Lakshmi Vyas
Author-email: lakshmi.vyas@gmail.com
License: MIT
Description: ============================
commando - argparse in style
============================
**Version 0.3.4**
A simple wrapper for ``argparse`` that allows commands and arguments
to be defined declaratively using decorators. Note that this does
not support all the features of ``argparse`` yet.
Commando also bundles a few utilities that are useful when building
command line applications.
Example
--------
Without commando::
def main():
parser = argparse.ArgumentParser(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__)
parser.add_argument('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
subcommands = parser.add_subparsers(title="Hyde commands",
description="Entry points for hyde")
init_command = subcommands.add_parser('init', help='Create a new hyde site')
init_command.set_defaults(run=init)
init_command.add_argument('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
init_command.add_argument('-f', '--force', action='store_true', default=False, dest='force',
help='Overwrite the current site if it exists')
args = parser.parse_args()
args.run(args)
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
With commando::
class Engine(Application):
@command(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
@param('-v', '--version', action='version', version='%(prog)s ' + __version__)
@param('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
def main(self, params): pass
@subcommand('init', help='Create a new hyde site')
@param('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
@param('-f', '--force', action='store_true', default=False, dest='overwrite',
help='Overwrite the current site if it exists')
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
Resources
---------
1. `Changelog`_
2. `License`_
3. `Contributing`_
4. `Authors`_
.. _Changelog: CHANGELOG.rst
.. _LICENSE: LICENSE
.. _Contributing: CONTRIBUTING.rst
.. _Authors: AUTHORS.rst
Keywords: argparse commandline utility
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: User Interfaces
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires: python (>= 2.7)
Provides: commando
commando-0.3.4/commando.egg-info/SOURCES.txt 0000644 0000765 0000024 00000000671 12151441026 022144 0 ustar lakshmivyas staff 0000000 0000000 AUTHORS.rst
CHANGELOG.rst
CONTRIBUTING.rst
LICENSE
README.rst
distribute_setup.py
setup.py
commando/__init__.py
commando/application.py
commando/conf.py
commando/util.py
commando.egg-info/PKG-INFO
commando.egg-info/SOURCES.txt
commando.egg-info/dependency_links.txt
commando.egg-info/top_level.txt
commando/tests/__init__.py
commando/tests/dev-req.txt
commando/tests/test_commando.py
commando/tests/test_conf.py
commando/tests/test_loader.py commando-0.3.4/commando.egg-info/top_level.txt 0000644 0000765 0000024 00000000011 12151441026 022776 0 ustar lakshmivyas staff 0000000 0000000 commando
commando-0.3.4/CONTRIBUTING.rst 0000644 0000765 0000024 00000003406 12133161710 017430 0 ustar lakshmivyas staff 0000000 0000000 **tl;dr** Good (code + tests + commit message) = Great Pull Request.
*****************************************************************************
How do the pull requests get merged?
------------------------------------
The following points are considered as part of merging pull requests after
it is deemed necessary.
1. Is there an issue tagged in the commit?
2. Do the existing tests pass?
3. Are there new tests added to verify any new functionality / issue?
4. Is the authors list up to date?
5. Is the changelog updated?
6. Is the version updated?
7. Does this require any changes to the documentation?
Guidelines
-----------
If the following guidelines are observed as much as possible, it will
immensely help in verifying and merging the pull requests.
1. One pull request = One feature or One bug.
2. Always tag an issue in the commit. If an issue does
not exist for a feature or a bug, please add one.
3. Use topic / feature branches.
4. Make sure a test exists to verify the committed code. A
good way to think about it is: if these commits were reversed
and only the test were added back in, it ought to fail.
5. Make the `commit message`_ as verbose as possible.
6. Add yourself to `Authors`_ list and update your contribution.
7. Cross update `Changelog`_ list as well.
8. Update ``version.py`` and ``README.rst`` with a version in this format:
-.
9. If the change was complicated and resulted in a lot of commits,
consider ``rebase -i`` to sqash and/or rearrange them to make it easier
to review.
10. Update the `Readme`_.
.. _commit message: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
.. _Changelog: CHANGELOG.rst
.. _Authors: AUTHORS.rst
.. _Readme: README.rst commando-0.3.4/distribute_setup.py 0000644 0000765 0000024 00000036615 12133161710 020747 0 ustar lakshmivyas staff 0000000 0000000 #!python
"""Bootstrap distribute installation
If you want to use setuptools in your package's setup.py, just include this
file in the same directory with it, and add this to the top of your setup.py::
from distribute_setup import use_setuptools
use_setuptools()
If you want to require a specific version of setuptools, set a download
mirror, or use an alternate download directory, you can do so by supplying
the appropriate options to ``use_setuptools()``.
This file can also be run as a script to install or upgrade setuptools.
"""
import os
import sys
import time
import fnmatch
import tempfile
import tarfile
from distutils import log
try:
from site import USER_SITE
except ImportError:
USER_SITE = None
try:
import subprocess
def _python_cmd(*args):
args = (sys.executable,) + args
return subprocess.call(args) == 0
except ImportError:
# will be used for python 2.3
def _python_cmd(*args):
args = (sys.executable,) + args
# quoting arguments if windows
if sys.platform == 'win32':
def quote(arg):
if ' ' in arg:
return '"%s"' % arg
return arg
args = [quote(arg) for arg in args]
return os.spawnl(os.P_WAIT, sys.executable, *args) == 0
DEFAULT_VERSION = "0.6.14"
DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/"
SETUPTOOLS_FAKED_VERSION = "0.6c11"
SETUPTOOLS_PKG_INFO = """\
Metadata-Version: 1.0
Name: setuptools
Version: %s
Summary: xxxx
Home-page: xxx
Author: xxx
Author-email: xxx
License: xxx
Description: xxx
""" % SETUPTOOLS_FAKED_VERSION
def _install(tarball):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# installing
log.warn('Installing Distribute')
if not _python_cmd('setup.py', 'install'):
log.warn('Something went wrong during the installation.')
log.warn('See the error message above.')
finally:
os.chdir(old_wd)
def _build_egg(egg, tarball, to_dir):
# extracting the tarball
tmpdir = tempfile.mkdtemp()
log.warn('Extracting in %s', tmpdir)
old_wd = os.getcwd()
try:
os.chdir(tmpdir)
tar = tarfile.open(tarball)
_extractall(tar)
tar.close()
# going in the directory
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
os.chdir(subdir)
log.warn('Now working in %s', subdir)
# building an egg
log.warn('Building a Distribute egg in %s', to_dir)
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
finally:
os.chdir(old_wd)
# returning the result
log.warn(egg)
if not os.path.exists(egg):
raise IOError('Could not build the egg.')
def _do_download(version, download_base, to_dir, download_delay):
egg = os.path.join(to_dir, 'distribute-%s-py%d.%d.egg'
% (version, sys.version_info[0], sys.version_info[1]))
if not os.path.exists(egg):
tarball = download_setuptools(version, download_base,
to_dir, download_delay)
_build_egg(egg, tarball, to_dir)
sys.path.insert(0, egg)
import setuptools
setuptools.bootstrap_install_from = egg
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, download_delay=15, no_fake=True):
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
was_imported = 'pkg_resources' in sys.modules or \
'setuptools' in sys.modules
try:
try:
import pkg_resources
if not hasattr(pkg_resources, '_distribute'):
if not no_fake:
_fake_setuptools()
raise ImportError
except ImportError:
return _do_download(version, download_base, to_dir, download_delay)
try:
pkg_resources.require("distribute>="+version)
return
except pkg_resources.VersionConflict:
e = sys.exc_info()[1]
if was_imported:
sys.stderr.write(
"The required version of distribute (>=%s) is not available,\n"
"and can't be installed while this script is running. Please\n"
"install a more recent version first, using\n"
"'easy_install -U distribute'."
"\n\n(Currently using %r)\n" % (version, e.args[0]))
sys.exit(2)
else:
del pkg_resources, sys.modules['pkg_resources'] # reload ok
return _do_download(version, download_base, to_dir,
download_delay)
except pkg_resources.DistributionNotFound:
return _do_download(version, download_base, to_dir,
download_delay)
finally:
if not no_fake:
_create_fake_setuptools_pkg_info(to_dir)
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
to_dir=os.curdir, delay=15):
"""Download distribute from a specified location and return its filename
`version` should be a valid distribute version number that is available
as an egg for download under the `download_base` URL (which should end
with a '/'). `to_dir` is the directory where the egg will be downloaded.
`delay` is the number of seconds to pause before an actual download
attempt.
"""
# making sure we use the absolute path
to_dir = os.path.abspath(to_dir)
try:
from urllib.request import urlopen
except ImportError:
from urllib2 import urlopen
tgz_name = "distribute-%s.tar.gz" % version
url = download_base + tgz_name
saveto = os.path.join(to_dir, tgz_name)
src = dst = None
if not os.path.exists(saveto): # Avoid repeated downloads
try:
log.warn("Downloading %s", url)
src = urlopen(url)
# Read/write all in one block, so we don't create a corrupt file
# if the download is interrupted.
data = src.read()
dst = open(saveto, "wb")
dst.write(data)
finally:
if src:
src.close()
if dst:
dst.close()
return os.path.realpath(saveto)
def _no_sandbox(function):
def __no_sandbox(*args, **kw):
try:
from setuptools.sandbox import DirectorySandbox
if not hasattr(DirectorySandbox, '_old'):
def violation(*args):
pass
DirectorySandbox._old = DirectorySandbox._violation
DirectorySandbox._violation = violation
patched = True
else:
patched = False
except ImportError:
patched = False
try:
return function(*args, **kw)
finally:
if patched:
DirectorySandbox._violation = DirectorySandbox._old
del DirectorySandbox._old
return __no_sandbox
def _patch_file(path, content):
"""Will backup the file then patch it"""
existing_content = open(path).read()
if existing_content == content:
# already patched
log.warn('Already patched.')
return False
log.warn('Patching...')
_rename_path(path)
f = open(path, 'w')
try:
f.write(content)
finally:
f.close()
return True
_patch_file = _no_sandbox(_patch_file)
def _same_content(path, content):
return open(path).read() == content
def _rename_path(path):
new_name = path + '.OLD.%s' % time.time()
log.warn('Renaming %s into %s', path, new_name)
os.rename(path, new_name)
return new_name
def _remove_flat_installation(placeholder):
if not os.path.isdir(placeholder):
log.warn('Unkown installation at %s', placeholder)
return False
found = False
for file in os.listdir(placeholder):
if fnmatch.fnmatch(file, 'setuptools*.egg-info'):
found = True
break
if not found:
log.warn('Could not locate setuptools*.egg-info')
return
log.warn('Removing elements out of the way...')
pkg_info = os.path.join(placeholder, file)
if os.path.isdir(pkg_info):
patched = _patch_egg_dir(pkg_info)
else:
patched = _patch_file(pkg_info, SETUPTOOLS_PKG_INFO)
if not patched:
log.warn('%s already patched.', pkg_info)
return False
# now let's move the files out of the way
for element in ('setuptools', 'pkg_resources.py', 'site.py'):
element = os.path.join(placeholder, element)
if os.path.exists(element):
_rename_path(element)
else:
log.warn('Could not find the %s element of the '
'Setuptools distribution', element)
return True
_remove_flat_installation = _no_sandbox(_remove_flat_installation)
def _after_install(dist):
log.warn('After install bootstrap.')
placeholder = dist.get_command_obj('install').install_purelib
_create_fake_setuptools_pkg_info(placeholder)
def _create_fake_setuptools_pkg_info(placeholder):
if not placeholder or not os.path.exists(placeholder):
log.warn('Could not find the install location')
return
pyver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
setuptools_file = 'setuptools-%s-py%s.egg-info' % \
(SETUPTOOLS_FAKED_VERSION, pyver)
pkg_info = os.path.join(placeholder, setuptools_file)
if os.path.exists(pkg_info):
log.warn('%s already exists', pkg_info)
return
log.warn('Creating %s', pkg_info)
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
pth_file = os.path.join(placeholder, 'setuptools.pth')
log.warn('Creating %s', pth_file)
f = open(pth_file, 'w')
try:
f.write(os.path.join(os.curdir, setuptools_file))
finally:
f.close()
_create_fake_setuptools_pkg_info = _no_sandbox(_create_fake_setuptools_pkg_info)
def _patch_egg_dir(path):
# let's check if it's already patched
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
if os.path.exists(pkg_info):
if _same_content(pkg_info, SETUPTOOLS_PKG_INFO):
log.warn('%s already patched.', pkg_info)
return False
_rename_path(path)
os.mkdir(path)
os.mkdir(os.path.join(path, 'EGG-INFO'))
pkg_info = os.path.join(path, 'EGG-INFO', 'PKG-INFO')
f = open(pkg_info, 'w')
try:
f.write(SETUPTOOLS_PKG_INFO)
finally:
f.close()
return True
_patch_egg_dir = _no_sandbox(_patch_egg_dir)
def _before_install():
log.warn('Before install bootstrap.')
_fake_setuptools()
def _under_prefix(location):
if 'install' not in sys.argv:
return True
args = sys.argv[sys.argv.index('install')+1:]
for index, arg in enumerate(args):
for option in ('--root', '--prefix'):
if arg.startswith('%s=' % option):
top_dir = arg.split('root=')[-1]
return location.startswith(top_dir)
elif arg == option:
if len(args) > index:
top_dir = args[index+1]
return location.startswith(top_dir)
if arg == '--user' and USER_SITE is not None:
return location.startswith(USER_SITE)
return True
def _fake_setuptools():
log.warn('Scanning installed packages')
try:
import pkg_resources
except ImportError:
# we're cool
log.warn('Setuptools or Distribute does not seem to be installed.')
return
ws = pkg_resources.working_set
try:
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools',
replacement=False))
except TypeError:
# old distribute API
setuptools_dist = ws.find(pkg_resources.Requirement.parse('setuptools'))
if setuptools_dist is None:
log.warn('No setuptools distribution found')
return
# detecting if it was already faked
setuptools_location = setuptools_dist.location
log.warn('Setuptools installation detected at %s', setuptools_location)
# if --root or --preix was provided, and if
# setuptools is not located in them, we don't patch it
if not _under_prefix(setuptools_location):
log.warn('Not patching, --root or --prefix is installing Distribute'
' in another location')
return
# let's see if its an egg
if not setuptools_location.endswith('.egg'):
log.warn('Non-egg installation')
res = _remove_flat_installation(setuptools_location)
if not res:
return
else:
log.warn('Egg installation')
pkg_info = os.path.join(setuptools_location, 'EGG-INFO', 'PKG-INFO')
if (os.path.exists(pkg_info) and
_same_content(pkg_info, SETUPTOOLS_PKG_INFO)):
log.warn('Already patched.')
return
log.warn('Patching...')
# let's create a fake egg replacing setuptools one
res = _patch_egg_dir(setuptools_location)
if not res:
return
log.warn('Patched done.')
_relaunch()
def _relaunch():
log.warn('Relaunching...')
# we have to relaunch the process
# pip marker to avoid a relaunch bug
if sys.argv[:3] == ['-c', 'install', '--single-version-externally-managed']:
sys.argv[0] = 'setup.py'
args = [sys.executable] + sys.argv
sys.exit(subprocess.call(args))
def _extractall(self, path=".", members=None):
"""Extract all members from the archive to the current working
directory and set owner, modification time and permissions on
directories afterwards. `path' specifies a different directory
to extract to. `members' is optional and must be a subset of the
list returned by getmembers().
"""
import copy
import operator
from tarfile import ExtractError
directories = []
if members is None:
members = self
for tarinfo in members:
if tarinfo.isdir():
# Extract directories with a safe mode.
directories.append(tarinfo)
tarinfo = copy.copy(tarinfo)
tarinfo.mode = 448 # decimal for oct 0700
self.extract(tarinfo, path)
# Reverse sort directories.
if sys.version_info < (2, 4):
def sorter(dir1, dir2):
return cmp(dir1.name, dir2.name)
directories.sort(sorter)
directories.reverse()
else:
directories.sort(key=operator.attrgetter('name'), reverse=True)
# Set correct owner, mtime and filemode on directories.
for tarinfo in directories:
dirpath = os.path.join(path, tarinfo.name)
try:
self.chown(tarinfo, dirpath)
self.utime(tarinfo, dirpath)
self.chmod(tarinfo, dirpath)
except ExtractError:
e = sys.exc_info()[1]
if self.errorlevel > 1:
raise
else:
self._dbg(1, "tarfile: %s" % e)
def main(argv, version=DEFAULT_VERSION):
"""Install or upgrade setuptools and EasyInstall"""
tarball = download_setuptools()
_install(tarball)
if __name__ == '__main__':
main(sys.argv[1:])
commando-0.3.4/LICENSE 0000644 0000765 0000024 00000002111 12133161710 015764 0 ustar lakshmivyas staff 0000000 0000000 The MIT License
Copyright (c) 2010-2013 Lakshmi Vyasarajan, Ringce.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
commando-0.3.4/PKG-INFO 0000644 0000765 0000024 00000010100 12151441026 016052 0 ustar lakshmivyas staff 0000000 0000000 Metadata-Version: 1.1
Name: commando
Version: 0.3.4
Summary: A declarative interface to argparse with additional utilities
Home-page: http://github.com/lakshmivyas/commando
Author: Lakshmi Vyas
Author-email: lakshmi.vyas@gmail.com
License: MIT
Description: ============================
commando - argparse in style
============================
**Version 0.3.4**
A simple wrapper for ``argparse`` that allows commands and arguments
to be defined declaratively using decorators. Note that this does
not support all the features of ``argparse`` yet.
Commando also bundles a few utilities that are useful when building
command line applications.
Example
--------
Without commando::
def main():
parser = argparse.ArgumentParser(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__)
parser.add_argument('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
subcommands = parser.add_subparsers(title="Hyde commands",
description="Entry points for hyde")
init_command = subcommands.add_parser('init', help='Create a new hyde site')
init_command.set_defaults(run=init)
init_command.add_argument('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
init_command.add_argument('-f', '--force', action='store_true', default=False, dest='force',
help='Overwrite the current site if it exists')
args = parser.parse_args()
args.run(args)
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
With commando::
class Engine(Application):
@command(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
@param('-v', '--version', action='version', version='%(prog)s ' + __version__)
@param('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
def main(self, params): pass
@subcommand('init', help='Create a new hyde site')
@param('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
@param('-f', '--force', action='store_true', default=False, dest='overwrite',
help='Overwrite the current site if it exists')
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
Resources
---------
1. `Changelog`_
2. `License`_
3. `Contributing`_
4. `Authors`_
.. _Changelog: CHANGELOG.rst
.. _LICENSE: LICENSE
.. _Contributing: CONTRIBUTING.rst
.. _Authors: AUTHORS.rst
Keywords: argparse commandline utility
Platform: UNKNOWN
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Topic :: Software Development :: User Interfaces
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires: python (>= 2.7)
Provides: commando
commando-0.3.4/README.rst 0000644 0000765 0000024 00000005352 12151440427 016465 0 ustar lakshmivyas staff 0000000 0000000 ============================
commando - argparse in style
============================
**Version 0.3.4**
A simple wrapper for ``argparse`` that allows commands and arguments
to be defined declaratively using decorators. Note that this does
not support all the features of ``argparse`` yet.
Commando also bundles a few utilities that are useful when building
command line applications.
Example
--------
Without commando::
def main():
parser = argparse.ArgumentParser(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__)
parser.add_argument('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
subcommands = parser.add_subparsers(title="Hyde commands",
description="Entry points for hyde")
init_command = subcommands.add_parser('init', help='Create a new hyde site')
init_command.set_defaults(run=init)
init_command.add_argument('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
init_command.add_argument('-f', '--force', action='store_true', default=False, dest='force',
help='Overwrite the current site if it exists')
args = parser.parse_args()
args.run(args)
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
With commando::
class Engine(Application):
@command(description='hyde - a python static website generator',
epilog='Use %(prog)s {command} -h to get help on individual commands')
@param('-v', '--version', action='version', version='%(prog)s ' + __version__)
@param('-s', '--sitepath', action='store', default='.', help="Location of the hyde site")
def main(self, params): pass
@subcommand('init', help='Create a new hyde site')
@param('-t', '--template', action='store', default='basic', dest='template',
help='Overwrite the current site if it exists')
@param('-f', '--force', action='store_true', default=False, dest='overwrite',
help='Overwrite the current site if it exists')
def init(self, params):
print params.sitepath
print params.template
print params.overwrite
Resources
---------
1. `Changelog`_
2. `License`_
3. `Contributing`_
4. `Authors`_
.. _Changelog: CHANGELOG.rst
.. _LICENSE: LICENSE
.. _Contributing: CONTRIBUTING.rst
.. _Authors: AUTHORS.rst
commando-0.3.4/setup.cfg 0000644 0000765 0000024 00000000073 12151441026 016606 0 ustar lakshmivyas staff 0000000 0000000 [egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
commando-0.3.4/setup.py 0000644 0000765 0000024 00000002210 12151440411 016467 0 ustar lakshmivyas staff 0000000 0000000 from distribute_setup import use_setuptools
use_setuptools()
from setuptools import setup
try:
long_description = open('README.rst', 'rt').read()
except IOError:
long_description = ''
setup(
name='commando',
description='A declarative interface to argparse with additional utilities',
long_description=long_description,
version='0.3.4',
author='Lakshmi Vyas',
author_email='lakshmi.vyas@gmail.com',
url='http://github.com/lakshmivyas/commando',
packages=['commando'],
requires=['python (>= 2.7)'],
provides=['commando'],
test_requires=['nose', 'mock', 'fswrap', 'markdown', 'yaml'],
license='MIT',
keywords=('argparse commandline utility'),
classifiers=[
'Development Status :: 3 - Alpha',
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Topic :: Software Development :: User Interfaces',
'Topic :: Software Development :: Libraries :: Python Modules',
],
test_suite='nose.collector',
)