Convenience functions for working with the Cmd module, the BaseCommand class for constructing command line programmes, and other command line related stuff.
Project description
Convenience functions for working with the Cmd module, the BaseCommand class for constructing command line programmes, and other command line related stuff.
Latest release 20240709:
BaseCommand: support putting the top level Usage in the class docstring instead of as .USAGE_FORMAT, append full usage to the class docstring in BaseCommand.__init_subclass__
.
Class BaseCommand
A 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 of BaseCommand
:
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 a main
method if the command has no subcommands
or a suite of cmd_
subcommand methods, 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 a self.options
attribute on which optional
modes are set,
avoiding conflict with the attributes of self
.
Subclasses with no subcommands
generally just implement a main(argv)
method.
Subclasses with subcommands
should implement a cmd_
subcommand(argv)
instance method
for each subcommand.
If a subcommand is itself implemented using BaseCommand
then it can be a simple attribute:
cmd_subthing = SubThingCommand
Returning to methods, if there is a paragraph in the method docstring
commencing with Usage:
then that paragraph is incorporated
into the main usage message automatically.
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 theopts
toself.options
.opts
is an(option,value)
sequence as returned bygetopot.getopt
. The default implementation iterates over these and callsapply_opt
.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.cmd_
subcmd(argv)
: if the command line options are followed by an argument whose value is subcmd, then the methodcmd_
subcmd(subcmd_argv)
will be called wheresubcmd_argv
contains the command line arguments following subcmd.main(argv)
: if there are nocmd_
subcmdmethods then method
main(argv)will be called where
argv` contains the command line arguments.
Editorial: why not arparse?
Primarily because when incorrectly invoked
an argparse command line prints the help/usage messgae
and aborts the whole programme with SystemExit
.
But also, I find the whole argparse add_argument
thing cumbersome.
Method BaseCommand.__init__(self, argv=None, *, cmd=None, options=None, **kw_options)
:
Initialise the command line.
Raises GetoptError
for unrecognised options.
Parameters:
argv
: optional command line arguments including the main command name ifcmd
is not specified. The default issys.argv
. The contents ofargv
are 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.Options
instance is allocated for use asoptions
. The defaultOptions
class isBaseCommandOptions
, a dataclass with some prefilled attributes and properties to aid use later. Other keyword arguments are applied toself.options
as attributes.
The cmd
and argv
parameters have some fiddly semantics for convenience.
There are 3 basic ways to initialise:
BaseCommand()
:argv
comes fromsys.argv
and the value forcmd
is derived fromargv[0]
BaseCommand(argv)
:argv
is the complete command line including the command name and the value forcmd
is derived fromargv[0]
BaseCommand(argv, cmd=foo)
:argv
is the command arguments after the command name andcmd
is set tofoo
The command line arguments are parsed according to
the optional GETOPT_SPEC
class attribute (default ''
).
If getopt_spec
is not empty
then apply_opts(opts)
is called
to apply the supplied options to the state
where opts
is the return from getopt.getopt(argv,getopt_spec)
.
After the option parse,
if the first command line argument foo
has a corresponding method cmd_
foo
then that argument is removed from the start of argv
and self.cmd_
foo(argv,options,cmd=
foo)
is called
and its value returned.
Otherwise self.main(argv,options)
is called
and its value returned.
If the command implementation requires some setup or teardown
then this may be provided by the run_context
context manager method,
called with cmd=
subcmd for subcommands
and with cmd=None
for main
.
BaseCommand.Options
Method BaseCommand.apply_opt(self, opt, val)
:
Handle an individual global command line option.
This default implementation raises a RuntimeError
.
It only fires if getopt
actually gathered arguments
and would imply that a GETOPT_SPEC
was supplied
without an apply_opt
or apply_opts
method to implement the options.
Method BaseCommand.apply_opts(self, opts)
:
Apply command line options.
Subclasses can override this
but it is usually easier to override apply_opt(opt,val)
.
Method BaseCommand.apply_preargv(self, argv)
:
Do any preparsing of argv
before the subcommand/main-args.
Return the remaining arguments.
This default implementation applies the default options
supported by self.options
(an instance of self.Options
class).
Method BaseCommand.cmd_help(self, argv)
:
Usage: {cmd} [-l] [subcommand-names...]
Print help for subcommands.
This outputs the full help for the named subcommands,
or the short help for all subcommands if no names are specified.
-l Long help even if no subcommand-names provided.
Method BaseCommand.cmd_shell(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10f792ac0>, **kw)
:
Usage: {cmd}
Run a command prompt via cmd.Cmd using this command's subcommands.
Method BaseCommand.cmdloop(self, intro=None)
:
Use cmd.Cmd
to run a command loop which calls the cmd_
* methods.
Method BaseCommand.getopt_error_handler(cmd, options, e, usage, subcmd=None)
:
The getopt_error_handler
method
is used to control the handling of GetoptError
s raised
during the command line parse
or during the main
or cmd_
subcmd` calls.
This default handler issues a warning containing the exception text,
prints the usage message to standard error,
and returns True
to indicate that the error has been handled.
The handler is called with these parameters:
cmd
: the command nameoptions
: theoptions
objecte
: theGetoptError
exceptionusage
: the command usage orNone
if this was not providedsubcmd
: optional subcommand name; if notNone
, is the name of the subcommand which caused the error
It returns a true value if the exception is considered handled,
in which case the main run
method returns 2.
It returns a false value if the exception is considered unhandled,
in which case the main run
method reraises the GetoptError
.
To let the exceptions out unhandled
this can be overridden with a method which just returns False
.
Otherwise,
the handler may perform any suitable action
and return True
to contain the exception
or False
to cause the exception to be reraised.
Method BaseCommand.handle_signal(self, sig, frame, *, runstate: Optional[cs.resources.RunState] = <function uses_runstate.<locals>.<lambda> at 0x10f792340>)
:
The default signal handler, which cancels the default RunState
.
Method BaseCommand.has_subcommands(self)
:
Test whether the class defines additional subcommands.
Method BaseCommand.method_cmdname(method_name: str)
:
The cmd
value from a method name.
Method BaseCommand.poparg(argv: List[str], *a, unpop_on_error=False)
:
Pop the leading argument off argv
and parse it.
Return the parsed argument.
Raises getopt.GetoptError
on a missing or invalid argument.
This is expected to be used inside a main
or cmd_*
command handler method or inside apply_preargv
.
You can just use:
value = argv.pop(0)
but this method provides conversion and valuation and a richer failure mode.
Parameters:
argv
: the argument list, which is modified in place withargv.pop(0)
- the argument list
argv
may be followed by some help text and/or an argument parser function. validate
: an optional function to validate the parsed value; this should return a true value if valid, or return a false value or raise aValueError
if invalidunvalidated_message
: an optional message aftervalidate
for values failing the validationunpop_on_error
: optional keyword parameter, defaultFalse
; if true then push the argument back onto the front ofargv
if it fails to parse;GetoptError
is still raised
Typical use inside a main
or cmd_*
method might look like:
self.options.word = self.poparg(argv, int, "a count value")
self.options.word = self.poparg(
argv, int, "a count value",
lambda count: count > 0, "count should be positive")
Because it raises GetoptError
on a bad argument
the normal usage message failure mode follows automatically.
Demonstration:
>>> argv = ['word', '3', 'nine', '4']
>>> BaseCommand.poparg(argv, "word to process")
'word'
>>> BaseCommand.poparg(argv, int, "count value")
3
>>> BaseCommand.poparg(argv, float, "length")
Traceback (most recent call last):
...
getopt.GetoptError: length 'nine': float('nine'): could not convert string to float: 'nine'
>>> BaseCommand.poparg(argv, float, "width", lambda width: width > 5)
Traceback (most recent call last):
...
getopt.GetoptError: width '4': invalid value
>>> BaseCommand.poparg(argv, float, "length")
Traceback (most recent call last):
...
getopt.GetoptError: length: missing argument
>>> argv = ['-5', 'zz']
>>> BaseCommand.poparg(argv, float, "size", lambda f: f>0, "size should be >0")
Traceback (most recent call last):
...
getopt.GetoptError: size '-5': size should be >0
>>> argv # -5 was still consumed
['zz']
>>> BaseCommand.poparg(argv, float, "size2", unpop_on_error=True)
Traceback (most recent call last):
...
getopt.GetoptError: size2 'zz': float('zz'): could not convert string to float: 'zz'
>>> argv # zz was pushed back
['zz']
Method BaseCommand.popopts(argv, attrfor=None, **opt_specs)
:
Parse option switches from argv
, a list of command line strings
with leading option switches.
Modify argv
in place and return a dict mapping switch names to values.
The optional positional argument attrfor
may supply an object whose attributes may be set by the options,
for example:
def cmd_foo(self, argv):
self.popopts(argv, self.options, a='all', j_=('jobs', int))
... use self.options.jobs etc ...
The expected options are specified by the keyword parameters
in opt_specs
:
- options not starting with a letter may be preceeded by an underscore
to allow use in the parameter list, for example
_1='once'
for a-1
option setting theonce
option name - a single letter name specifies a short option and a multiletter name specifies a long option
- options requiring an argument have a trailing underscore
- options not requiring an argument normally imply a value
of
True
; if their synonym commences with a dash they will imply a value ofFalse
, for examplen='dry_run',y='-dry_run'
The BaseCommandOptions
class provides a popopts
method
which is a shim for this method with attrfor=self
i.e.
the options object.
So common use in a command method usually looks like this:
class SomeCommand(BaseCommand):
def cmd_foo(self, argv):
options = self.options
# accept a -j or --jobs options
options.popopts(argv, jobs=1, j='jobs')
print("jobs =", options.jobs)
The self.options
object is preprovided as an instance of
the self.Options
class, which is BaseCommandOptions
by
default. This presupplies support for some basic options
like -v
for "verbose" and so forth, and a subcommand
need not describe these in a call to self.options.popopts()
.
Example:
>>> import os.path
>>> from typing import Optional
>>> @dataclass
... class DemoOptions(BaseCommandOptions):
... all: bool = False
... jobs: int = 1
... number: int = 0
... once: bool = False
... path: Optional[str] = None
... trace_exec: bool = False
...
>>> options = DemoOptions()
>>> argv = ['-1', '-v', '-y', '-j4', '--path=/foo', 'bah', '-x']
>>> opt_dict = options.popopts(
... argv,
... _1='once',
... a='all',
... j_=('jobs',int),
... x='-trace_exec',
... y='-dry_run',
... dry_run=None,
... path_=(str, os.path.isabs, 'not an absolute path'),
... verbose=None,
... )
>>> opt_dict
{'once': True, 'verbose': True, 'dry_run': False, 'jobs': 4, 'path': '/foo'}
>>> options # doctest: +ELLIPSIS
DemoOptions(cmd=None, dry_run=False, force=False, quiet=False, runstate_signals=(...), verbose=True, all=False, jobs=4, number=0, once=True, path='/foo', trace_exec=False)
Method BaseCommand.repl(self, *argv, banner=None, local=None)
:
Run an interactive Python prompt with some predefined local names.
Aka REPL (Read Evaluate Print Loop).
Parameters:
argv
: any notional command line argumentsbanner
: optional banner stringlocal
: optional local names mapping
The default local
mapping is a dict
containing:
argv
: fromargv
options
: fromself.options
self
: fromself
- the attributes of
options
- the attributes of
self
This is not presented automatically as a subcommand, but commands wishing such a command should provide something like this:
def cmd_repl(self, argv):
""" Usage: {cmd}
Run an interactive Python prompt with some predefined local names.
"""
return self.repl(*argv)
Method BaseCommand.run(self, **kw_options)
:
Run a command.
Returns the exit status of the command.
May raise GetoptError
from subcommands.
Any keyword arguments are used to override self.options
attributes
for the duration of the run,
for example to presupply a shared Upd
from an outer context.
If the first command line argument foo
has a corresponding method cmd_
foo
then that argument is removed from the start of argv
and self.cmd_
foo(cmd=
foo)
is called
and its value returned.
Otherwise self.main(argv)
is called
and its value returned.
If the command implementation requires some setup or teardown
then this may be provided by the run_context()
context manager method.
Method BaseCommand.run_context(*a, upd: Optional[cs.upd.Upd] = <function uses_upd.<locals>.<lambda> at 0x10f792480>, **kw)
:
The context manager which surrounds main
or cmd_
subcmd.
This default does several things, and subclasses should override it like this:
@contextmanager
def run_context(self):
with super().run_context():
try:
... subclass context setup ...
yield
finally:
... any unconditional cleanup ...
Method BaseCommand.subcommand_usage_text(self, subcmd, usage_format_mapping=None, short=False)
:
Return the usage text for a subcommand.
Parameters:
subcmd
: the subcommand nameshort
: just include the first line of the usage message, intented for when there are many subcommands
BaseCommand.subcommands
Method BaseCommand.usage_text(self, *, cmd=None, format_mapping=None, short=False, show_subcmds=None)
:
Compute the "Usage:" message for this class
from the top level USAGE_FORMAT
and the 'Usage:'
-containing docstrings of its cmd_*
methods.
Parameters:
cmd
: optional command name, default derived from the class nameformat_mapping
: an optional format mapping for filling in format strings in the usage textshort
: defaultFalse
; if true then just provide the opening sentenceshow_subcmds
: constrain the usage to particular subcommands named inshow_subcmds
; this is used to produce a shorter usage for subcommand usage failures
Class BaseCommandCmd(cmd.Cmd)
A cmd.Cmd
subclass used to provide interactive use of a
command's subcommands.
The BaseCommand.cmdloop()
class method instantiates an
instance of this and calls its .cmdloop()
method
i.e. cmd.Cmd.cmdloop
.
Class BaseCommandOptions(cs.threads.HasThreadState)
A base class for the BaseCommand
options
object.
This is the default class for the self.options
object
available during BaseCommand.run()
,
and available as the BaseCommand.Options
attribute.
Any keyword arguments are applied as field updates to the instance.
It comes prefilled with:
.dry_run=False
.force=False
.quiet=False
.verbose=False
and a.doit
property which is the inverse of.dry_run
.
It is recommended that if ``BaseCommandsubclasses use a different type for their
Options` 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 ...
Method BaseCommandOptions.__call__(self, **updates)
:
Calling the options object returns a context manager whose
value is a shallow copy of the options with any suboptions
applied.
Example showing the semantics:
>>> from cs.cmdutils import BaseCommandOptions
>>> @dataclass
... class DemoOptions(BaseCommandOptions):
... x: int = 0
...
>>> options = DemoOptions(x=1)
>>> assert options.x == 1
>>> assert not options.verbose
>>> with options(verbose=True) as subopts:
... assert options is not subopts
... assert options.x == 1
... assert not options.verbose
... assert subopts.x == 1
... assert subopts.verbose
...
>>> assert options.x == 1
>>> assert not options.verbose
Method BaseCommandOptions.as_dict(self)
:
Return the optionas as a dict
.
Method BaseCommandOptions.copy(self, **updates)
:
Return a new instance of BaseCommandOptions
(well, type(self)
)
which is a shallow copy of the public attributes from self.__dict__
.
Any keyword arguments are applied as attribute updates to the copy.
Property BaseCommandOptions.doit
:
I usually use a doit
flag,
the inverse of dry_run
.
BaseCommandOptions.perthread_state
Method BaseCommandOptions.popopts(self, argv, **opt_specs)
:
Convenience method to appply BaseCommand.popopts
to the options (self
).
Example for a BaseCommand
cmd_foo
method:
def cmd_foo(self, argv):
self.options.popopts(
c_='config',
l='long',
x='trace',
)
if self.options.dry_run:
print("dry run!")
The class attribute COMMON_OPT_SPECS
is a mapping of
options which are always supported. BaseCommandOptions
has: COMMON_OPT_SPECS={'n': 'dry_run', 'q': 'quiet', 'v': 'verbose'}
.
A subclass with more common options might extend this like so,
from cs.hashindex
:
COMMON_OPT_SPECS = dict(
e='ssh_exe',
h_='hashname',
H_='hashindex_exe',
**BaseCommand.Options.COMMON_OPT_SPECS,
)
Method BaseCommandOptions.update(self, **updates)
:
Modify the options in place with the mapping updates
.
It would be more normal to call the options in a with
statement
as shown for __call__
.
Function docmd(dofunc)
Decorator for cmd.Cmd
subclass methods
to supply some basic quality of service.
This decorator:
- wraps the function call in a
cs.pfx.Pfx
for context - intercepts
getopt.GetoptError
s, issues awarning
and runsself.do_help
with the method name, then returnsNone
- intercepts other
Exception
s, issues anexception
log message and returnsNone
The intended use is to decorate cmd.Cmd
do_
* methods:
from cmd import Cmd
from cs.cmdutils import docmd
...
class MyCmd(Cmd):
@docmd
def do_something(...):
... do something ...
Function extract_usage_from_doc(doc: str | None, usage_marker='Usage:') -> Tuple[str, str]
Extract a "Usage:"
paragraph from a docstring
and return the unindented usage and the docstring with that paragraph elided.
If the usage paragraph is not present, return (None,doc)
.
Class SubCommand
An implementation for a subcommand.
Method SubCommand.__call__(self, argv: List[str])
:
Run the subcommand.
Parameters:
argv
: the command line arguments after the subcommand name
Method SubCommand.default_usage(self)
:
Return '{cmd} [options...]'
or '{cmd} subcommand [options...]'
.
Method SubCommand.get_subcmds(self)
:
Return the names of self.method
's subcommands in lexical order.
Method SubCommand.get_subcommands(self)
:
Return self.method
's mapping of subcommand name to SubCommand
.
Method SubCommand.get_usage_format(self) -> str
:
Return the usage format string for this subcommand.
Note: no leading "Usage:" prefix.
This first tries self.method.USAGE_FORMAT
, falling back
to deriving it from obj_docstring(self.method)
.
Usually a subcommand which is another BaseCommand
instance
will have a .USAGE_FORMAT
attribute and a subcommand which
is a method will derive the usage from its docstring.
When deriving from the docstring we look for a paragraph
commencing with the string Usage:
and otherwise fall back
to its first parapgraph.
Method SubCommand.get_usage_keywords(self)
:
Return a mapping to be used when formatting the usage format string.
Property SubCommand.instance
:
An instance of the class for self.method
.
Method SubCommand.usage_text(self, *, short: bool, recurse: bool = False, show_subcmds: Union[bool, str, List[str], NoneType] = None, usage_mapping: Optional[Mapping] = None) -> str
:
Return the filled out usage text for this subcommand.
Function uses_cmd_options(func, cls=<class 'cs.cmdutils.BaseCommandOptions'>, options_param_name='options')
A decorator to provide a default parameter containing the
prevailing BaseCommandOptions
instance as the options
keyword
argument, using the cs.deco.default_params
decorator factory.
This allows functions to utilitse global options set by a
command such as options.dry_run
or options.verbose
without
the tedious plumbing through the entire call stack.
Parameters:
cls
: theBaseCommandOptions
orBaseCommand
class, defaultBaseCommandOptions
. If aBaseCommand
subclass is provided itscls.Options
class is used.options_param_name
: the parameter name to provide, defaultoptions
Examples:
@uses_cmd_options
def f(x,*,options):
""" Run directly from the prevailing options. """
if options.verbose:
print("doing f with x =", x)
....
@uses_cmd_options
def f(x,*,verbose=None,options):
""" Get defaults from the prevailing options. """
if verbose is None:
verbose = options.verbose
if verbose:
print("doing f with x =", x)
....
Release Log
Release 20240709:
BaseCommand: support putting the top level Usage in the class docstring instead of as .USAGE_FORMAT, append full usage to the class docstring in BaseCommand.__init_subclass__
.
Release 20240630:
- BaseCommand: make SUBCOMMAND_ARGV_DEFAULT be 'shell' for an interactive prompt, still a little unsure how sensible this is, aiming at the very cool submode stuff from the Cisco switch config command line.
- BaseCommandOptions: new as_dict() method.
- New SubCommand.usage_text() to compose the full usage text for this SubCommand.
- Many small improvements and internal refactors.
Release 20240519: BaseCommand.run_context: attach the runstate to the options.
Release 20240422:
- BaseCommandOptions.popopts: return the dict from BaseCommand.popopts().
- BaseCommand.apply_preargv: apply the default options supported by self.options.
- BaseCommandOptions.update(mapping) method, useful for dropping subcommand-specific defaults onto the options ahead of the local popopts() call.
Release 20240412:
- BaseCommand.run_context: do not store .upd and .runstate on the options (it confuses options in subcommands and we have @uses_runstate and @uses_upd forthis anyway these days).
- BaseCommand.run_context: catch SIGQUIT, present the default handler as BaseCommand.handle_signal.
Release 20240316:
- New @uses_cmd_options decorator to provide an "options" parameter being the prevailing BaseCommandOptions instance.
- BaseCommandOptions.popopts: get common options from BaseCommandOptions.COMMON_OPT_SPECS.
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.
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 for init_subclass docstring update.
Release 20210407:
- BaseCommand.init_subclass: behave sanely if the subclass has no initial doc.
- 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 former
options
parameter from self.options. - BaseCommand: provide default
apply_opt
andapply_opts
methods; 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, drop cmd
from arguments passed to main/cmd_* methods (present in options
).
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file cs_cmdutils-20240709.tar.gz
.
File metadata
- Download URL: cs_cmdutils-20240709.tar.gz
- Upload date:
- Size: 54.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0f83532c692e263b687251d76c011abb3fadc6e58a20f499aa16c07082a8d467 |
|
MD5 | a988097a0522b82c7c63a344ca781423 |
|
BLAKE2b-256 | 527a3a5a398a089476d19812dfbb90c9cde8c997c07ca5ff56d560d525390510 |
File details
Details for the file cs.cmdutils-20240709-py3-none-any.whl
.
File metadata
- Download URL: cs.cmdutils-20240709-py3-none-any.whl
- Upload date:
- Size: 27.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.0
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0f4a0a3f05b8b05da87954658fa3abaf515752b4e333dc4aad5223d7362f18ad |
|
MD5 | c89b6fc8ad0aecddffa7afaaeded361c |
|
BLAKE2b-256 | f9bc3e50408e0962146be380c741d48d1d5828915c15fc94b8ab46d534ca11d4 |