def __init__(self, executable): if self.PROGNAME is None: self.PROGNAME = os.path.basename(executable) if self.DESCRIPTION is None: self.DESCRIPTION = inspect.getdoc(self) self.executable = executable self._switches_by_name = {} self._switches_by_func = {} self._subcommands = {} for cls in reversed(type(self).mro()): for obj in cls.__dict__.values(): if isinstance(obj, Subcommand): if obj.name.startswith("-"): raise SubcommandError( "Subcommand names cannot start with '-'") # it's okay for child classes to override subcommands set by their parents self._subcommands[obj.name] = obj continue swinfo = getattr(obj, "_switch_info", None) if not swinfo: continue for name in swinfo.names: if name in self._unbound_switches: continue if name in self._switches_by_name and not self._switches_by_name[ name].overridable: raise SwitchError( "Switch %r already defined and is not overridable" % (name, )) self._switches_by_name[name] = swinfo self._switches_by_func[swinfo.func] = swinfo
def invoke(cls, *args, **switches): """Invoke this application programmatically (as a function), in the same way ``run()`` would. There are two key differences: the return value of ``main()`` is not converted to an integer (returned as-is), and exceptions are not swallowed either. :param args: any positional arguments for ``main()`` :param switches: command-line switches are passed as keyword arguments, e.g., ``foo=5`` for ``--foo=5`` """ inst = cls("") swfuncs = {} for index, (swname, val) in enumerate(switches.items(), 1): switch = getattr(cls, swname) swinfo = inst._switches_by_func[switch._switch_info.func] if isinstance(switch, CountOf): p = (range(val), ) elif swinfo.list and not hasattr(val, "__iter__"): raise SwitchError("Switch %r must be a sequence (iterable)" % (swname, )) elif not swinfo.argtype: # a flag if val not in (True, False, None, Flag): raise SwitchError("Switch %r is a boolean flag" % (swname, )) p = () else: p = (val, ) swfuncs[swinfo.func] = SwitchParseInfo(swname, p, index) ordered, tailargs = inst._validate_args(swfuncs, args) for f, a in ordered: f(inst, *a) cleanup = None if not inst.nested_command or inst.CALL_MAIN_IF_NESTED_COMMAND: retcode = inst.main(*tailargs) cleanup = functools.partial(inst.cleanup, retcode) if not retcode and inst.nested_command: subapp, argv = inst.nested_command subapp.parent = inst inst, retcode = subapp.run(argv, exit=False) if cleanup: cleanup() return inst, retcode
def _parse_kwd_args(self, switches): """Parses keywords (positional arguments), used by invoke.""" swfuncs = {} for index, (swname, val) in enumerate(switches.items(), 1): switch = getattr(type(self), swname) swinfo = self._switches_by_func[switch._switch_info.func] if isinstance(switch, CountOf): p = (range(val),) elif swinfo.list and not hasattr(val, "__iter__"): raise SwitchError("Switch %r must be a sequence (iterable)" % (swname,)) elif not swinfo.argtype: # a flag if val not in (True, False, None, Flag): raise SwitchError("Switch %r is a boolean flag" % (swname,)) p = () else: p = (val,) swfuncs[swinfo.func] = SwitchParseInfo(swname, p, index) return swfuncs
def __init__(self, executable): # Filter colors if self.PROGNAME is None: self.PROGNAME = os.path.basename(executable) elif isinstance(self.PROGNAME, colors._style): self.PROGNAME = self.PROGNAME | os.path.basename(executable) elif colors.filter(self.PROGNAME) == '': self.PROGNAME = colors.extract( self.PROGNAME) | os.path.basename(executable) if self.DESCRIPTION is None: self.DESCRIPTION = getdoc(self) # Allow None for the colors self.COLOR_GROUPS = defaultdict( lambda: colors.do_nothing, dict() if type(self).COLOR_GROUPS is None else type(self).COLOR_GROUPS) if type(self).COLOR_USAGE is None: self.COLOR_USAGE = colors.do_nothing self.executable = executable self._switches_by_name = {} self._switches_by_func = {} self._switches_by_envar = {} self._subcommands = {} for cls in reversed(type(self).mro()): for obj in cls.__dict__.values(): if isinstance(obj, Subcommand): name = colors.filter(obj.name) if name.startswith("-"): raise SubcommandError( "Subcommand names cannot start with '-'") # it's okay for child classes to override subcommands set by their parents self._subcommands[name] = obj continue swinfo = getattr(obj, "_switch_info", None) if not swinfo: continue for name in swinfo.names: if name in self._unbound_switches: continue if name in self._switches_by_name and not self._switches_by_name[ name].overridable: raise SwitchError( "Switch %r already defined and is not overridable" % (name, )) self._switches_by_name[name] = swinfo self._switches_by_func[swinfo.func] = swinfo if swinfo.envname: self._switches_by_envar[swinfo.envname] = swinfo
def __init__(self, executable): # Convert the colors to plumbum.colors on the instance (class remains the same) for item in ('COLOR_PROGNAME', 'COLOR_DISCRIPTION', 'COLOR_VERSION', 'COLOR_HEADING', 'COLOR_USAGE', 'COLOR_SUBCOMMANDS'): setattr(self, item, colors(getattr(type(self), item))) self.COLOR_GROUPS = defaultdict(lambda: colors()) self.COLOR_GROUPS_BODY = defaultdict(lambda: colors()) for item in type(self).COLOR_GROUPS: self.COLOR_GROUPS[item] = colors(type(self).COLOR_GROUPS[item]) for item in type(self).COLOR_GROUPS_BODY: self.COLOR_GROUPS_BODY[item] = colors( type(self).COLOR_GROUPS_BODY[item]) if self.PROGNAME is None: self.PROGNAME = os.path.basename(executable) if self.DESCRIPTION is None: self.DESCRIPTION = inspect.getdoc(self) self.executable = executable self._switches_by_name = {} self._switches_by_func = {} self._switches_by_envar = {} self._subcommands = {} for cls in reversed(type(self).mro()): for obj in cls.__dict__.values(): if isinstance(obj, Subcommand): if obj.name.startswith("-"): raise SubcommandError( "Subcommand names cannot start with '-'") # it's okay for child classes to override subcommands set by their parents self._subcommands[obj.name] = obj continue swinfo = getattr(obj, "_switch_info", None) if not swinfo: continue for name in swinfo.names: if name in self._unbound_switches: continue if name in self._switches_by_name and not self._switches_by_name[ name].overridable: raise SwitchError( "Switch %r already defined and is not overridable" % (name, )) self._switches_by_name[name] = swinfo self._switches_by_func[swinfo.func] = swinfo if swinfo.envname: self._switches_by_envar[swinfo.envname] = swinfo
def _parse_args(self, argv): tailargs = [] swfuncs = {} index = 0 while argv: index += 1 a = argv.pop(0) val = None if a == "--": # end of options, treat the rest as tailargs tailargs.extend(argv) break if a in self._subcommands: subcmd = self._subcommands[a].get() self.nested_command = (subcmd, [self.PROGNAME + " " + a] + argv) break elif a.startswith("--") and len(a) >= 3: # [--name], [--name=XXX], [--name, XXX], [--name, ==, XXX], # [--name=, XXX], [--name, =XXX] eqsign = a.find("=") if eqsign >= 0: name = a[2:eqsign] argv.insert(0, a[eqsign:]) else: name = a[2:] swname = "--" + name if name not in self._switches_by_name: raise UnknownSwitch("Unknown switch %s" % (swname, )) swinfo = self._switches_by_name[name] if swinfo.argtype: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname, )) a = argv.pop(0) if a and a[0] == "=": if len(a) >= 2: val = a[1:] else: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname)) val = argv.pop(0) else: val = a elif a.startswith("-") and len(a) >= 2: # [-a], [-a, XXX], [-aXXX], [-abc] name = a[1] swname = "-" + name if name not in self._switches_by_name: raise UnknownSwitch("Unknown switch %s" % (swname, )) swinfo = self._switches_by_name[name] if swinfo.argtype: if len(a) >= 3: val = a[2:] else: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname, )) val = argv.pop(0) elif len(a) >= 3: argv.insert(0, "-" + a[2:]) else: if a.startswith("-"): raise UnknownSwitch("Unknown switch %s" % (a, )) tailargs.append(a) continue # handle argument val = self._handle_argument(val, swinfo, name) if swinfo.func in swfuncs: if swinfo.list: swfuncs[swinfo.func].val[0].append(val) else: if swfuncs[swinfo.func].swname == swname: raise SwitchError("Switch %r already given" % (swname, )) else: raise SwitchError( "Switch %r already given (%r is equivalent)" % (swfuncs[swinfo.func].swname, swname)) else: if swinfo.list: swfuncs[swinfo.func] = SwitchParseInfo( swname, ([val], ), index) elif val is NotImplemented: swfuncs[swinfo.func] = SwitchParseInfo(swname, (), index) else: swfuncs[swinfo.func] = SwitchParseInfo( swname, (val, ), index) # Extracting arguments from environment variables envindex = 0 for env, swinfo in self._switches_by_envar.items(): envindex -= 1 envval = local.env.get(env) if envval is None: continue if swinfo.func in swfuncs: continue # skip if overridden by command line arguments val = self._handle_argument(envval, swinfo, env) envname = "$%s" % (env, ) if swinfo.list: # multiple values over environment variables are not supported, # this will require some sort of escaping and separator convention swfuncs[swinfo.func] = SwitchParseInfo(envname, ([val], ), envindex) elif val is NotImplemented: swfuncs[swinfo.func] = SwitchParseInfo(envname, (), envindex) else: swfuncs[swinfo.func] = SwitchParseInfo(envname, (val, ), envindex) return swfuncs, tailargs
def _parse_args(self, argv): tailargs = [] swfuncs = {} index = 0 while argv: index += 1 a = argv.pop(0) if a == "--": # end of options, treat the rest as tailargs tailargs.extend(argv) break if a in self._subcommands: subcmd = self._subcommands[a].get() self.nested_command = (subcmd, [self.PROGNAME + " " + a] + argv) break elif a.startswith("--") and len(a) >= 3: # [--name], [--name=XXX], [--name, XXX], [--name, ==, XXX], # [--name=, XXX], [--name, =XXX] eqsign = a.find("=") if eqsign >= 0: name = a[2:eqsign] argv.insert(0, a[eqsign:]) else: name = a[2:] swname = "--" + name if name not in self._switches_by_name: raise UnknownSwitch("Unknown switch %s" % (swname, )) swinfo = self._switches_by_name[name] if swinfo.argtype: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname, )) a = argv.pop(0) if a and a[0] == "=": if len(a) >= 2: val = a[1:] else: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname)) val = argv.pop(0) else: val = a elif a.startswith("-") and len(a) >= 2: # [-a], [-a, XXX], [-aXXX], [-abc] name = a[1] swname = "-" + name if name not in self._switches_by_name: raise UnknownSwitch("Unknown switch %s" % (swname, )) swinfo = self._switches_by_name[name] if swinfo.argtype: if len(a) >= 3: val = a[2:] else: if not argv: raise MissingArgument( "Switch %s requires an argument" % (swname, )) val = argv.pop(0) elif len(a) >= 3: argv.insert(0, "-" + a[2:]) else: if a.startswith("-"): raise UnknownSwitch("Unknown switch %s" % (a, )) tailargs.append(a) continue # handle argument if swinfo.argtype: try: val = swinfo.argtype(val) except (TypeError, ValueError): ex = sys.exc_info()[1] # compat raise WrongArgumentType( "Argument of %s expected to be %r, not %r:\n %r" % (swname, swinfo.argtype, val, ex)) else: val = NotImplemented if swinfo.func in swfuncs: if swinfo.list: swfuncs[swinfo.func].val[0].append(val) else: if swfuncs[swinfo.func].swname == swname: raise SwitchError("Switch %r already given" % (swname, )) else: raise SwitchError( "Switch %r already given (%r is equivalent)" % (swfuncs[swinfo.func].swname, swname)) else: if swinfo.list: swfuncs[swinfo.func] = SwitchParseInfo( swname, ([val], ), index) elif val is NotImplemented: swfuncs[swinfo.func] = SwitchParseInfo(swname, (), index) else: swfuncs[swinfo.func] = SwitchParseInfo( swname, (val, ), index) return swfuncs, tailargs