commando-1.0.0/0000755000175000017500000000000012566545174014426 5ustar navilannavilan00000000000000commando-1.0.0/CONTRIBUTING.rst0000644000175000017500000000337712566545155017100 0ustar navilannavilan00000000000000**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-1.0.0/commando.egg-info/0000755000175000017500000000000012566545174017715 5ustar navilannavilan00000000000000commando-1.0.0/commando.egg-info/dependency_links.txt0000644000175000017500000000000112566545174023763 0ustar navilannavilan00000000000000 commando-1.0.0/commando.egg-info/PKG-INFO0000644000175000017500000001005312566545174021011 0ustar navilannavilan00000000000000Metadata-Version: 1.1 Name: commando Version: 1.0.0 Summary: A declarative interface to argparse and extras. Home-page: http://github.com/hyde/commando Author: Lakshmi Vyas Author-email: lakshmi.vyas@gmail.com License: MIT Description: ============================ commando - argparse in style ============================ **Version 1.0.0** 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-1.0.0/commando.egg-info/top_level.txt0000644000175000017500000000001112566545174022437 0ustar navilannavilan00000000000000commando commando-1.0.0/commando.egg-info/SOURCES.txt0000644000175000017500000000067112566545174021605 0ustar navilannavilan00000000000000AUTHORS.rst CHANGELOG.rst CONTRIBUTING.rst LICENSE README.rst setup.py commando/__init__.py commando/_compat.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-1.0.0/commando/0000755000175000017500000000000012566545174016223 5ustar navilannavilan00000000000000commando-1.0.0/commando/__init__.py0000644000175000017500000000010512566333210020312 0ustar navilannavilan00000000000000# pylint: disable-msg=C0111,W0401 from commando.application import * commando-1.0.0/commando/application.py0000644000175000017500000002162212566545155021102 0ustar navilannavilan00000000000000# -*- coding: utf-8 -*- """ Declarative interface for argparse """ from argparse import ArgumentParser from collections import namedtuple from commando.util import getLoggerWithConsoleHandler from ._compat import itervalues, with_metaclass 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 itervalues(attrs): 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(with_metaclass(Commando, object)): """ Barebones base class for command line applications. """ 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` """ # pylint: disable-msg=E1101 return self.__parser__.print_usage(out_file) # pylint: enable-msg=E1101 def print_help(self, out_file=None): """ Delegates to `ArgumentParser.print_help` """ # pylint: disable-msg=E1101 return self.__parser__.print_help(out_file) # pylint: enable-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 as e: # pylint: disable-msg=W0703 import traceback self.logger.debug(traceback.format_exc()) self.logger.error(str(e)) if self.raise_exceptions: raise sys.exit(2) commando-1.0.0/commando/util.py0000644000175000017500000001301312566545155017547 0ustar navilannavilan00000000000000""" 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-1.0.0/commando/conf.py0000644000175000017500000000767312566545155017536 0ustar navilannavilan00000000000000""" A few wrappers and utilities for handling complex configuration objects. """ from collections import defaultdict from ._compat import with_metaclass, iteritems 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 iteritems(initial): 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 iteritems(overrides): 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 iteritems(cattrs) if getattr(member, 'autoprop', False)} for name, member in iteritems(autoprops): cattrs[name] = AutoPropDescriptor(member) return super(AutoPropMetaClass, mcs).__new__( mcs, cname, cbases, cattrs) # pylint: disable-msg=R0903 class AutoProp(with_metaclass(AutoPropMetaClass, 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' """ @staticmethod def default(function): """ Decorator for autoprops. """ function.autoprop = True return function commando-1.0.0/commando/_compat.py0000644000175000017500000000444212566545155020222 0ustar navilannavilan00000000000000"""Python 2 and 3 compatibility module.""" # Note: borrowed from https://github.com/dirn/Simon/ import sys # Development should be done with Python 3 first. Rather than checking # for Python 3 and creating special cases for it, check for Python 2 and # create special cases for it instead. PY2 = sys.version_info[0] == 2 if PY2: # Define everything that is Python 2 specific. # dictionary iterators _iteritems = 'iteritems' _iterkeys = 'iterkeys' _itervalues = 'itervalues' # other iterators def get_next(x): return x.next range = xrange # NOQA, Python 2 only # types str_types = (str, unicode) # NOQA, Python 2 only exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') else: # Define everything that is Python 3 specific. # dictionary iterators _iteritems = 'items' _iterkeys = 'keys' _itervalues = 'values' # other iterators def get_next(x): return x.__next__ range = range # types str_types = (str,) def reraise(tp, value, tb=None): if getattr(value, '__traceback__', tb) is not tb: raise value.with_traceback(tb) raise value def iteritems(d, *args, **kwargs): return iter(getattr(d, _iteritems)(*args, **kwargs)) def iterkeys(d, *args, **kwargs): return iter(getattr(d, _iterkeys)(*args, **kwargs)) def itervalues(d, *args, **kwargs): return iter(getattr(d, _itervalues)(*args, **kwargs)) def with_metaclass(meta, *bases): # This requires a bit of explanation: the basic idea is to make a # dummy metaclass for one level of class instantiation that replaces # itself with the actual metaclass. Because of internal type checks # we also need to make sure that we downgrade the custom metaclass # for one level to something closer to type (that's why __call__ and # __init__ comes back from type etc.). # # This has the advantage over six.with_metaclass in that it does not # introduce dummy classes into the final MRO. class metaclass(meta): __call__ = type.__call__ __init__ = type.__init__ def __new__(cls, name, this_bases, d): if this_bases is None: return type.__new__(cls, name, (), d) return meta(name, bases, d) return metaclass('DummyMetaClass', None, {}) commando-1.0.0/commando/tests/0000755000175000017500000000000012566545174017365 5ustar navilannavilan00000000000000commando-1.0.0/commando/tests/__init__.py0000644000175000017500000000000012566333210021446 0ustar navilannavilan00000000000000commando-1.0.0/commando/tests/test_commando.py0000644000175000017500000002046712566541117022575 0ustar navilannavilan00000000000000# -*- coding: utf-8 -*- """ Use nose `$ pip install nose` `$ pip install mock` `$ nosetests test_commando.py -d -v` """ from commando import * from mock import patch 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 patch.object(ComplexCommandLine, '_main'): with patch.object(ComplexCommandLine, '_sub'): c = ComplexCommandLine() c.parse(['--help']) @trap_exit_fail def test_command_subcommands(): with patch.object(ComplexCommandLine, '_main') as _main: with patch.object(ComplexCommandLine, '_sub') as _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 not params.__dict__ 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 patch.object(EmptyCommandLine, '_main') as _main: with patch.object(EmptyCommandLine, '_sub') as _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 patch.object(EmptyCommandLine, '_main') as _main: with patch.object(EmptyCommandLine, '_sub') as _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 patch.object(NestedCommandLine, '_main') as _main, \ patch.object(NestedCommandLine, '_sub') as _sub, \ patch.object(NestedCommandLine, '_foobar_bla') as _foobar_bla, \ patch.object(NestedCommandLine, '_foobar_blip_blop') as _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-1.0.0/commando/tests/test_conf.py0000644000175000017500000000370412566333210021711 0ustar navilannavilan00000000000000from 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-1.0.0/commando/tests/test_loader.py0000644000175000017500000000203512566333210022226 0ustar navilannavilan00000000000000# -*- 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-1.0.0/commando/tests/dev-req.txt0000644000175000017500000000002712566333210021452 0ustar navilannavilan00000000000000mock==1.0.1 nose==1.2.1commando-1.0.0/PKG-INFO0000644000175000017500000001005312566545174015522 0ustar navilannavilan00000000000000Metadata-Version: 1.1 Name: commando Version: 1.0.0 Summary: A declarative interface to argparse and extras. Home-page: http://github.com/hyde/commando Author: Lakshmi Vyas Author-email: lakshmi.vyas@gmail.com License: MIT Description: ============================ commando - argparse in style ============================ **Version 1.0.0** 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-1.0.0/setup.cfg0000644000175000017500000000007312566545174016247 0ustar navilannavilan00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 commando-1.0.0/AUTHORS.rst0000644000175000017500000000200712566541117016275 0ustar navilannavilan00000000000000------------------------- 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) * `Jon Banafato`_ - Upgrade from distribute to setuptools - Python 3 compatibility .. _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 .. _Jon Banafato: https://github.com/jonafato commando-1.0.0/setup.py0000644000175000017500000000206612566545155016143 0ustar navilannavilan00000000000000from setuptools import setup try: long_description = open('README.rst', 'rt').read() except IOError: long_description = '' setup( name='commando', description='A declarative interface to argparse and extras.', long_description=long_description, version='1.0.0', author='Lakshmi Vyas', author_email='lakshmi.vyas@gmail.com', url='http://github.com/hyde/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', ) commando-1.0.0/CHANGELOG.rst0000644000175000017500000000407612566545155016455 0ustar navilannavilan00000000000000Version 1.0.0 ------------- - Fix code formatting issues. - Add travis.yml. Version 0.3.5a -------------- Thanks to `Jon Banafato`_: - Install with setuptools instead of distribute. - Python 3 compatibility. 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 .. _Jon Banafato: https://github.com/jonafato commando-1.0.0/LICENSE0000644000175000017500000000211112566333210015410 0ustar navilannavilan00000000000000The 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-1.0.0/README.rst0000644000175000017500000000535212566545155016121 0ustar navilannavilan00000000000000============================ commando - argparse in style ============================ **Version 1.0.0** 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