commando-0.3.4/0000755000076500000240000000000012151441026014765 5ustar lakshmivyasstaff00000000000000commando-0.3.4/AUTHORS.rst0000644000076500000240000000160012133161710016640 0ustar lakshmivyasstaff00000000000000------------------------- 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.rst0000644000076500000240000000347212151440605017016 0ustar lakshmivyasstaff00000000000000Version 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/0000755000076500000240000000000012151441026016562 5ustar lakshmivyasstaff00000000000000commando-0.3.4/commando/__init__.py0000644000076500000240000000010512151422232020665 0ustar lakshmivyasstaff00000000000000# pylint: disable-msg=C0111,W0401 from commando.application import * commando-0.3.4/commando/application.py0000644000076500000240000002132612151440411021440 0ustar lakshmivyasstaff00000000000000# -*- 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.py0000644000076500000240000000762512151425234020076 0ustar lakshmivyasstaff00000000000000""" 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/0000755000076500000240000000000012151441026017724 5ustar lakshmivyasstaff00000000000000commando-0.3.4/commando/tests/__init__.py0000644000076500000240000000000012133161710022022 0ustar lakshmivyasstaff00000000000000commando-0.3.4/commando/tests/dev-req.txt0000644000076500000240000000002712133161710022026 0ustar lakshmivyasstaff00000000000000mock==1.0.1 nose==1.2.1commando-0.3.4/commando/tests/test_commando.py0000644000076500000240000002076012133161710023136 0ustar lakshmivyasstaff00000000000000# -*- 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.py0000644000076500000240000000370412133161710022265 0ustar lakshmivyasstaff00000000000000from 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.py0000644000076500000240000000203512133161710022602 0ustar lakshmivyasstaff00000000000000# -*- 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 Falsecommando-0.3.4/commando/util.py0000644000076500000240000001311212151425405020112 0ustar lakshmivyasstaff00000000000000""" 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/0000755000076500000240000000000012151441026020254 5ustar lakshmivyasstaff00000000000000commando-0.3.4/commando.egg-info/dependency_links.txt0000644000076500000240000000000112151441026024322 0ustar lakshmivyasstaff00000000000000 commando-0.3.4/commando.egg-info/PKG-INFO0000644000076500000240000001010012151441026021341 0ustar lakshmivyasstaff00000000000000Metadata-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.txt0000644000076500000240000000067112151441026022144 0ustar lakshmivyasstaff00000000000000AUTHORS.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.pycommando-0.3.4/commando.egg-info/top_level.txt0000644000076500000240000000001112151441026022776 0ustar lakshmivyasstaff00000000000000commando commando-0.3.4/CONTRIBUTING.rst0000644000076500000240000000340612133161710017430 0ustar lakshmivyasstaff00000000000000**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.rstcommando-0.3.4/distribute_setup.py0000644000076500000240000003661512133161710020747 0ustar lakshmivyasstaff00000000000000#!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/LICENSE0000644000076500000240000000211112133161710015764 0ustar lakshmivyasstaff00000000000000The 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-INFO0000644000076500000240000001010012151441026016052 0ustar lakshmivyasstaff00000000000000Metadata-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.rst0000644000076500000240000000535212151440427016465 0ustar lakshmivyasstaff00000000000000============================ 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.cfg0000644000076500000240000000007312151441026016606 0ustar lakshmivyasstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 commando-0.3.4/setup.py0000644000076500000240000000221012151440411016467 0ustar lakshmivyasstaff00000000000000from 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', )