Skip to content

Instantly share code, notes, and snippets.

@mikelikespie
Created July 28, 2012 05:34
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mikelikespie/3191937 to your computer and use it in GitHub Desktop.
Save mikelikespie/3191937 to your computer and use it in GitHub Desktop.
Interface for Argparse Similar to Declarative base
import abc
import re
import argparse
class ArgBaseError(Exception): pass
def _inject_vals(name, bases, dict):
expected_args = {}
is_subcommand = False
for b in bases:
if b is Command:
dict['__parser__'] = argparse.ArgumentParser()
elif issubclass(b, Command):
if '__subparsers__' not in b.__dict__:
setattr(b, '__subparsers__', b.__parser__.add_subparsers())
b.__parser__._defaults.pop('command')
if '__subcommand__' not in dict:
raise ArgBaseError(name + " must have __subcommand__ defined")
if hasattr(b, '__expectedargs__'):
expected_args.update(b.__expectedargs__)
subparser = b.__subparsers__.add_parser(dict['__subcommand__'],
help=dict.get('__doc__'))
is_subcommand = True
dict['__parser__'] = subparser
parser = dict['__parser__']
arglist = [(v.ordinal, k, v)
for k,v
in dict.iteritems()
if isinstance(v, Arg)]
# Go through all the base classes that aren't command and find attrs in
# them. These are mixins
for b in bases:
if b is not Command and not issubclass(b, Command):
b_args = [(v.ordinal, n, v)
for n,v
in ((n,getattr(b, n))
for n
in dir(b))
if isinstance(v, Arg)]
arglist += b_args
expected_args.update((k,v) for _,k,v in b_args)
arglist.sort()
for _, name, arg in arglist:
arg_names = arg.args or (name,)
var_name = arg_names[0].lstrip('-').replace('-', '_')
expected_args[var_name] = name
parser.add_argument(*arg_names, **arg.kwargs)
dict['__expectedargs__'] = expected_args
return is_subcommand
class Ordered(object):
_object_counter = 0
def __init__(self):
self.ordinal = self._object_counter
self._object_counter += 1
class MetaBase(abc.ABCMeta):
def __new__(mcs, name, bases, dict):
is_sub = False
if name is not 'Command':
is_sub = _inject_vals(name, bases, dict)
cls = abc.ABCMeta.__new__(mcs, name, bases, dict)
if name is not 'Command':
def run_command(args):
instance = cls(args)
return instance()
cls.__parser__.set_defaults(command=run_command)
return cls
class Command(Ordered):
__metaclass__ = MetaBase
def __call__(self):
pass
def __init__(self, args):
self.args = args
for arg_name, var_name in self.__expectedargs__.iteritems():
setattr(self, var_name, getattr(args, arg_name))
@classmethod
def main(cls, *args):
args = cls.__parser__.parse_args(*args)
args.command(args)
class Arg(Ordered):
def __init__(self, *args, **kwargs):
Ordered.__init__(self)
self.args = args;
self.kwargs = kwargs
__all__ = ['Command', 'Arg']
"""You can use mixins to use same properties in different apps"""
from argbase import *
import git
class FooValueMixin(object):
foo_value = Arg(default='HEAD', nargs='?', help='Check that treelike is valid [default %(default)s]')
class CheckSignoffs(Command, FooValueMixin):
disallow_safe_merge = Arg('--disallow-safe-merge',
action='store_true',
default=False,
help='any merge will fail if not signed off on')
def __call__(self):
print "Would check signoffs for foo_value %s" % self.foo_value
print "Allow safe merge: %s", self.disallow_safe_merge
class CheckTests(Command, FooValueMixin):
def __call__(self):
print "Would check tests for foo_value %s" % self.foo_value
# Try each one:
CheckSignoffs.main()
"""Example of argbase working for a singe command"""
from argbase import *
class SingleCommand(Command):
file = Arg('--file', nargs='?')
def __call__(self):
print "I am a single command"
SingleCommand.main()
"""Test with hierarchy"""
from argbase import *
class Base(Command):
file = Arg('--file', nargs='?', default='some_file.txt')
class SubCommandOne(Base):
"""Subcommand 1 help"""
__subcommand__ = 'subcommand'
sub_arg = Arg()
class HelloCommand(Base):
"""Prints hello or FOO"""
__subcommand__ = 'say-hi'
fun_arg = Arg('--foo', nargs='?')
abcd = Arg('--gggg', nargs='?')
def __call__(self):
print "Arg inherited from base class: ", self.file
print self.fun_arg or "hello"
Base.main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment