Package: cs.cmdutils
Convenience functions for working with the Cmd module,
the BaseCommand class for constructing command line programmes,
and other command line related stuff.Latest release 20240211:Include the first sentence of the subcommand description in the short help.BaseCommandOptions: move the runstate_signals into this directly.BaseCommand: move the run() options stuff into run_context() and have it work on a copy of the original options.BaseCommandCmd: implement get_names(), provide docstrings for the do_* attributes, thus help.BaseCommand.run_context: make runstate and upd keyword only parameters.ClassBaseCommandA base class for handling nestable command lines.This class provides the basic parse and dispatch mechanisms
for command lines.
To implement a command line
one instantiates a subclass ofBaseCommand:class MyCommand(BaseCommand):
    GETOPT_SPEC = 'ab:c'
    USAGE_FORMAT = r"""Usage: {cmd} [-a] [-b bvalue] [-c] [--] arguments...
      -a    Do it all.
      -b    But using bvalue.
      -c    The 'c' option!
    """
    ...and provides either amainmethod if the command has no subcommands
or a suite ofcmd_subcommandmethods, one per subcommand.Running a command is done by:MyCommand(argv).run()Modules which implement a command line mode generally look like this:... imports etc ...
def main(argv=None, **run_kw):
    """ The command line mode.
    """
    return MyCommand(argv).run(**run_kw)
... other code ...
class MyCommand(BaseCommand):
... other code ...
if __name__ == '__main__':
    sys.exit(main(sys.argv))Instances have aself.optionsattribute on which optional
modes are set,
avoiding conflict with the attributes ofself.Subclasses with no subcommands
generally just implement amain(argv)method.Subclasses with subcommands
should implement acmd_subcommand(argv)instance method
for each subcommand.
If a subcommand is itself implemented usingBaseCommandthen it can be a simple attribute:cmd_subthing = SubThingCommandReturning to methods, if there is a paragraph in the method docstring
commencing withUsage:then that paragraph is incorporated automatically
into the main usage message.
Example:def cmd_ls(self, argv):
    """ Usage: {cmd} [paths...]
          Emit a listing for the named paths.

        Further docstring non-usage information here.
    """
    ... do the "ls" subcommand ...The subclass is customised by overriding the following methods:apply_opt(opt,val):
apply an individual getopt global command line option
toself.options.apply_opts(opts):
apply theoptstoself.options.optsis an(option,value)sequence
as returned bygetopot.getopt.
The default implementation iterates over these and callsapply_opt.cmd_subcmd(argv):
if the command line options are followed by an argument
whose value issubcmd,
then the methodcmd_subcmd(subcmd_argv)will be called wheresubcmd_argvcontains the command line arguments
followingsubcmd.main(argv):
if there are no command line arguments after the options
or the first argument does not have a correspondingcmd_subcmdmethod
then methodmain(argv)will be called whereargvcontains the command line arguments.run_context():
a context manager to provide setup or teardown actions
to occur before and after the command implementation respectively,
such as to open and close a database.Editorial: why not arparse?
Primarily because when incorrectly invoked
an argparse command line prints the help/usage messgae
and aborts the whole programme withSystemExit.
But also, I find the whole argparseadd_argumentthing cumbersome.MethodBaseCommand.__init__(self, argv=None, *, cmd=None, options=None, **kw_options):
Initialise the command line.
RaisesGetoptErrorfor unrecognised options.Parameters:argv:
optional command line arguments
including the main command name ifcmdis not specified.
The default issys.argv.
The contents ofargvare copied,
permitting desctructive parsing ofargv.cmd:
optional keyword specifying the command name for context;
if this is not specified it is taken fromargv.pop(0).options:
an optional keyword providing object for command state and context.
If not specified a newself.Optionsinstance
is allocated for use asoptions.
The defaultOptionsclass isBaseCommandOptions,
a dataclass with some prefilled attributes and properties
to aid use later.
Other keyword arguments are applied toself.optionsas attributes.Thecmdandargvparameters have some fiddly semantics for convenience.
There are 3 basic ways to initialise:BaseCommand():argvcomes fromsys.argvand the value forcmdis derived fromargv[0]BaseCommand(argv):argvis the complete command line
including the command name and the value forcmdis
derived fromargv[0]BaseCommand(argv, cmd=foo):argvis the command
argumentsafterthe command name andcmdis set tofooThe command line arguments are parsed according to
the optionalGETOPT_SPECclass attribute (default'').
Ifgetopt_specis not empty
thenapply_opts(opts)is called
to apply the supplied options to the state
whereoptsis the return fromgetopt.getopt(argv,getopt_spec).After the option parse,
if the first command line argumentfoohas a corresponding methodcmd_foothen that argument is removed from the start ofargvandself.cmd_foo(argv,options,cmd=foo)is called
and its value returned.
Otherwiseself.main(argv,options)is called
and its value returned.If the command implementation requires some setup or teardown
then this may be provided by therun_contextcontext manager method,
called withcmd=subcmdfor subcommands
and withcmd=Noneformain.ClassBaseCommandCmd(cmd.Cmd)Acmd.Cmdsubclass used to provide interactive use of a
command's subcommands.TheBaseCommand.cmdloop()class method instantiates an
instance of this cand calls its.cmdloop()method
i.e.cmd.Cmd.cmdloop.ClassBaseCommandOptions(cs.threads.HasThreadState, cs.context.ContextManagerMixin)A base class for theBaseCommandoptionsobject.This is the default class for theself.optionsobject
available duringBaseCommand.run(),
and available as theBaseCommand.Optionsattribute.Any keyword arguments are applied as field updates to the instance.It comes prefilled with:.dry_run=False.force=False.quiet=False.verbose=Falseand a.doitproperty which is the inverse of.dry_run.It is recommended that if ``BaseCommandsubclasses use a different type for theirOptions` that it should be a
subclass of `BaseCommandOptions`.
Since `BaseCommandOptions` is a data class, this typically looks like:@dataclass
class Options(BaseCommand.Options):
    ... optional extra fields etc ...Functiondocmd(dofunc)Decorator forcmd.Cmdsubclass methods
to supply some basic quality of service.This decorator:wraps the function call in acs.pfx.Pfxfor contextinterceptsgetopt.GetoptErrors, issues awarningand runsself.do_helpwith the method name,
then returnsNoneintercepts otherExceptions,
issues anexceptionlog message
and returnsNoneThe intended use is to decoratecmd.Cmddo_* methods:from cmd import Cmd
from cs.cmdutils import docmd
...
class MyCmd(Cmd):
    @docmd
    def do_something(...):
        ... do something ...Release LogRelease 20240211:Include the first sentence of the subcommand description in the short help.BaseCommandOptions: move the runstate_signals into this directly.BaseCommand: move the run() options stuff into run_context() and have it work on a copy of the original options.BaseCommandCmd: implement get_names(), provide docstrings for the do_* attributes, thus help.BaseCommand.run_context: make runstate and upd keyword only parameters.Release 20240201:BaseCommand.run: catch CancellationError and return 1.BaseCommandCmd.getattr: recognise EOF, exit and quit to terminate the cmdloop.Release 20231129:
BaseCommandOptions: define a runstate field.Release 20230703:
Small internal changes.Release 20230612:BaseCommand.cmdloop: fix intro parameter.Other small fixes.Release 20230407:BaseCommand: use @uses_runstate when preparing the command, store as self.options.runstate.Make BaseCommandOptions a data class.Drop any pretence at python 2 support, we're long past that.BaseCommand: new cmdloop method to run a cmd.Cmd instance to run subcommand interactively.BaseCommand: rename shell to repl, add cmd_shell to call cmdloop().Drop BaseCommand.apply_defaults in favour of the Options dataclass.BaseCommand: do setup_logging before initiating the Options instance.Release 20230212:BaseCommand.run_context: update RunState support.BaseCommand.run_context: always be having an self.options.upd.Release 20230211:
BaseCommand: new shell() method to present an interactive Python prompt for use by subclasses cmd_shell method if desired.Release 20221228:
Move a lot of the context logic from BaseCommand.run to BaseCommand.run_context, which now must be correctly overridden in subclasses.Release 20220918:BaseCommand.run_context: expand default signals to include SIGHUP, expose as BaseCommand.DEFAULT_SIGNALS.BaseCommand.run: pass in the subclass handle_signal method if present.Release 20220626:BaseCommand.poparg: fix positional argument handling.BaseCommand.poparg: new unpop_on_error=False parameter to support pushing a bad argument back onto the front of the argument list.Release 20220606:
BaseCommand.run: remove the Upd bodge, too annoying, now fixed in cs.upd I believe.Release 20220605:BaseCommand: new popopts(argv,...) compact getopt wrapper.BaseCommand: new poparg(argv,...) compact validating argument consumer.BaseCommand: drop run_argv, provided no utility.BaseCommand.run: get the RunState signal list from self.options.runstate_signals.BaseCommand.apply_opts: support multiple individual options raising GetoptError, as I hate commands which abort at the first bad option.Assorted other small things.Release 20220429:BaseCommand: fold dots in argv[0] into underscores, supports subcommands like "setup.py".BaseCommand: new popargv(argv[,help_text[,parse[,validate[,unvalidated_message]]]]) helper class method.BaseCommand: accept dashed-form of the underscored_form subcommand name.BaseCommand: new self.options.runstate_signals=SIGINT,SIGTERM specifying singals to catch-and-cancel, shuffle run() context managers.Release 20220318:
BaseCommand.init: handle main() method in the New Scheme.Release 20220315:
_BaseSubCommand.init: hook in the class USAGE_KEYWORDS for methods.Release 20220311:
BaseCommand: big refactor of subcommand internals and make the "cmd_foo=FooCommand" implementation work properly.Release 20211208:
BaseCommand: better handle an unknown subcommand.Release 20210927:Usage: show only the per subcommand usage for in-subcommand GetoptError.Usage: show terse usage when the subcommand cannot be recognised.Usage: support bare -h, -help, --help.Release 20210913:
New BaseCommand.apply_preargv method to gather special arguments before subcommands.Release 20210906:BaseCommand.cmd_help: bugfix obsolete parameter list.BaseCommand.SUBCOMMAND_ARGV_DEFAULT: support a single str value, turn into list.Release 20210809:
Bugfix BaseCommand.cmd_help for modern API.Release 20210731:BaseCommand.run: apply optional keyword arguments to self.options during the run.Look for self.SUBCOMMAND_ARGV_DEFAULT if no subcommand is supplied.Bugfix case for "main" method and no "cmd_*" methods.Bugfix BaseCommand.cmd_help.Release 20210420:BaseCommand.getopt_error_handler: replace error print() with warning().Docstring improvements.Release 20210407.1:
BaseCommand: bugfix forinit_subclassdocstring update.Release 20210407:BaseCommand.init_subclass: behave sanely if the subclass has no initialdoc.BaseCommand: new .run_argv convenience method, obviates the "def main" boilerplate.Release 20210404:
BaseCommand subclasses: automatically add the main usage message to the subclass docstring.Release 20210306:BREAKING CHANGE: rework BaseCommand as a more normal class instantiated with argv and with most methods being instance methods, getting the formeroptionsparameter from self.options.BaseCommand: provide defaultapply_optandapply_optsmethods; subclasses will generally just override the former.Release 20210123:
BaseCommand: propagate the format mapping (cmd, USAGE_KEYWORDS) to the subusage generation.Release 20201102:BaseCommand.cmd_help: supply usage only for "all commands", full docstring for specified commands.BaseCommand: honour presupplied options.log_level.BaseCommand.usage_text: handle missing USAGE_FORMAT better.BaseCommand.run: provide options.upd.BaseCommand subclasses may now override BaseCommand.OPTIONS_CLASS (default SimpleNamespace) in order to provide convenience methods on the options.BaseCommand.run: separate variable for subcmd with dash translated to underscore to match method names.Minor fixes.Release 20200615:
BaseCommand.usage_text: do not mention the "help" command if it is the only subcommand (it won't be available if there are no other subcommands).Release 20200521.1:
Fix DISTINFO.install_requires.Release 20200521:BaseCommand.run: support using BaseCommand subclasses as cmd_* names to make it easy to nest BaseCommands.BaseCommand: new hack_postopts_argv method called after parsing the main command line options, for inferring subcommands or the like.BaseCommand: extract "Usage:" paragraphs from subcommand method docstrings to build the main usage message.BaseCommand: new cmd_help default command.Assorted bugfixes and small improvements.Release 20200318:BaseCommand.run: make argv optional, get additional usage keywords from self.USAGE_KEYWORDS.@BaseCommand.add_usage_to_docstring: honour cls.USAGE_KEYWORDS.BaseCommand: do not require GETOPT_SPEC for commands with no defined options.BaseCommand.run: call cs.logutils.setup_logging.Release 20200229:
Improve subcommand selection logic, replace StackableValues with stackattrs, dropcmdfrom arguments passed to main/cmd_* methods (present inoptions).Release 20200210:New BaseCommand.add_usage_to_docstring class method to be called after class setup, to append the usage message to the class docstring.BaseCommand.run: remove spurious Pfx(cmd), as logutils does this for us already.Release 20190729:
BaseCommand: support for a USAGE_FORMAT usage message format string and a getopt_error_handler method.Release 20190619.1:
Another niggling docstring formatting fix.Release 20190619:
Minor documentation updates.Release 20190617.2:
Lint.Release 20190617.1:
Initial release with @docmd decorator and alpha quality BaseCommand command line assistance class.