argread
: Parse command-line arguments and options¶
This class provides the ArgReader
class, instances of
which can be used to customize available command-line options for a
given interface.
The ArgReader
class has the function ArgReader.parse()
,
which interprets sys.argv
and splits it into a list of args and
kwargs. The base ArgReader
class will parse command-line
arguments in useful ways, but one of the main intents is to subclass
ArgReader
and customize how various CLI arguments are parsed.
Here are some examples of the base interpreter, which can also be used
with the convenience function readkeys()
.
>>> readkeys(["prog", "arg1", "arg2", "-v", "--name", "argread"])
("arg1", "arg2"), {"v": True, "name": "argread"}
>>> readkeys(["prog", "-v", "1", "2", "-name", "argread", "d=3"])
("2",), {"v": "1", "name": "argread", "d": "3"}
Notice that the 1
goes as the value of the option v
. The 2
is not preceded by a str
starting with a dash, so it gets
interpreted as an arg.
There is an alternate function readflags()
that interprets
multi-letter args starting with a single dash differently:
>>> readflags(["prog", "-lh", "fname"])
("fname",), {"l": True, "h": True}
There is a third function readflagstar()
that interprets arguments
like tar
>>> readflagstar(["prog", "-cvf", "fname"])
(), {"c": True, "v": True, "f": "fname"}
These convenience functions make it easy to parse many command-line
inputs with minimal effort, but it is also possible to declare richer
and more specific interfaces by subclassing ArgReader
.
For example
class MyParser(ArgReader):
_optlist_noval = (
"default",
"verbose",
)
_optmap = {
"d": "default",
"v": verbose",
}
has two properties. First -d
and -v
are abbreviations for
--default
and --verbose
, respectively, because of the entries in
MyParser._optmap
. Second, neither option can take a “value”, so
$ prog -d hub url
would be parsed as
("hub", "url"), {"default": True}
In other words, the word hub
didn’t get interpreted as the value for
the option d
as it would with the default ArgReader
.
Another common use case is to convert CLI strings to another type. For
example suppose you have an option i
that takes in the index of
something you want to work with, like
$ prog -i 3
The default readkeys()
in this case would return
(), {"i": "3"}
you can convert this str
"3"
to an int
with the
following subclass
class MyConverter(ArgReader):
_optconverters = {
"i": int,
}
This ArgReader
also allows for convenient and powerful parsing
of functions arguments. See for example
def f(a, b, **kw):
...
Users of this module create subclasses of ArgReader
that
process the expected keyword arguments to f()
. The
argument-parsing capabilities of ArgReader
include
only allowing specific keys (
ArgReader._optlist
)mapping kwargs to alternate names, e.g. using v as a shortcut for verbose (
ArgReader._optmap
)specifying the type(s) allowed for specific options (
ArgReader._opttypes
)creating aliases for values (
ArgReader._optvalmap
)calling converter functions (e.g.
int()
to convert astr
to anint
) (ArgReader._optconverters
)
Suppose you have a function
def f(a, b, **kw):
...
Where a should be a str
, b should be an int
, and
the only kwargs are verbose and help, which should both be
bool
. However, users can use h as an alias for help and v
for verbose. Then we could write a subclass of ArgReader
to
parse and validate args to this function.
class FKwargs(ArgReader):
_optlist = ("help", "verbose")
_optmap = {
"h": "help",
"v": "verbose",
}
_opttypes = {
"a": str,
"b": int,
"help": bool,
"verbose": bool,
}
_arglist = ("a", "b")
_nargmin = 2
_nargmax = 2
Here is how this parser handles an example with expected inputs.
>>> opts = FKwargs("me", 33, v=True)
>>> print(opts)
{'verbose': True}
>>> print(opts.get_argvals())
('me', 33)
In many cases it is preferable to use
FLOAT_TYPES
instead offloat
,BOOL_TYPES
instead ofbool
, and
within ArgReader._opttypes
, e.g.
class FKwargs(ArgReader):
_optlist = ("help", "verbose")
_optmap = {
"h": "help",
"v": "verbose",
}
_opttypes = {
"a": str,
"b": INT_TYPES,
"help": BOOL_TYPES,
"verbose": BOOL_TYPES,
}
_arglist = ("a", "b")
_nargmin = 2
_nargmax = 2
so that values taken from numpy
arrays are also recognized as
valid “integers,” “floats,” or “booleans.”
Here are some examples of how FKwargs might handle bad inputs.
>>> FKwargs("my", help=True)
File "argread.py", line 172, in wrapper
raise err.__class__(msg) from None
argread.ArgReadTypeError: FKwargs() takes 2 arguments, but 1 were given
>>> FKwargs(2, 3)
File "argread.py", line 172, in wrapper
raise err.__class__(msg) from None
argread.ArgReadTypeError: FKwargs() arg 0 (name='a'): got type 'int';
expected 'str'
>>> FKwargs("my", 10, b=True)
File "argread.py", line 172, in wrapper
raise err.__class__(msg) from None
argread.ArgReadNameError: FKwargs() unknown kwarg 'b'
>>> FKwargs("my", 10, h=1)
File "argread.py", line 172, in wrapper
raise err.__class__(msg) from None
argread.ArgReadTypeError: FKwargs() kwarg 'help': got type 'int';
expected 'bool'
In order to use an instance of this FKwargs
there are several
approaches. The first is to call the parser class directly:
def f(a, b, **kw):
opts = FKwargs(a, b, **kw)
...
Another method is to use FKwargs as a decorator
@FKwargs.parse
def f(a, b, **kw):
...
The decorator option ensures that a, b, and kw have all been
validated. Users can then use kw.get("help")
without needing to
check for h.
- class cape.argread.ArgReader(*args, **kw)¶
Class to parse command-line interface arguments
- Call:
>>> parser = ArgReader(**kw)
- Outputs:
- parser:
ArgReader
Instance of command-line argument parser
- parser:
- Attributes:
- apply_argconverter(j: int, argname: str, rawval: Any)¶
Apply option converter function to raw positional arg value
- Call:
>>> val = opts.apply_argconverter(j, argname, rawval)
- Inputs:
- Outputs:
- val: {rawval} |
object
Result of calling optconverter for opt on rawval
- val: {rawval} |
- Raises:
ArgReadTypeError
if optconverter for opt is not callable
- apply_optconverter(opt: str, rawval: Any)¶
Apply option converter function to raw value
- Call:
>>> val = opts.apply_optconverter(opt, rawval)
- Inputs:
- Outputs:
- val: {rawval} |
object
Result of calling optconverter for opt on rawval
- val: {rawval} |
- Raises:
ArgReadTypeError
if optconverter for opt is not callable
- classmethod check(func: Callable)¶
Decorator for a function to parse and validate its inputs
- Call:
>>> wrapper = cls.parse(func)
- Example:
@cls.check def func(*a, **kw): ...
- Inputs:
- func:
callable
A function, class, or callable instance
- func:
- Outputs:
- check_argtype(j: int, argname: str, val: Any)¶
Check type of positional arg after conversion function
- Call:
>>> opts.check_argtype(opt, val)
- Inputs:
- Raises:
ArgReadTypeError
if val has wrong type
- check_argval(j: int, argname: str, val: Any)¶
Check positional arg value against list of recognized values
- Call:
>>> opts.check_optval(opt, rawval)
- Inputs:
- Raises:
ArgReadValueError
if argname has an optval setting and val is not in it
- check_rawargtype(j: int, argname: str, rawval: Any)¶
Check type of positional arg prior to conversion function
- Call:
>>> opts.check_rawargtype(opt, rawval)
- Inputs:
- Raises:
ArgReadTypeError
if rawval has wrong type
- decide_cmdname(argv: list | None = None) SubCmdTuple ¶
Identify sub-command if appropriate
- Call:
>>> cmdname, subargv = parser.decide_cmdname(argv=None)
- Inputs:
- Outputs:
- equal_sign_key = True¶
Option to allow equal-sign options, e.g.
key=val
becomes{"key": "val"}
- fullparse(argv: list | None = None) SubParserTuple ¶
Identify sub-command and use appropriate parser
- Call:
>>> cmdname, subparser = parser.fullparse(argv=None)
- Inputs:
- Outputs:
- cmdname:
None
|str
Name of command, if identified or inferred
- subparser:
ArgReadder
Parser for cmdname applied to remaining CLI args
- cmdname:
- fullparse_check(argv: list | None = None) SubParserCheck ¶
Identify sub-command and use appropriate parser
- Call:
>>> cmdname, subparser, ierr = parser.fullparse_check(argv)
- Inputs:
- Outputs:
- get_argdict() dict ¶
Get dictionary of all args and kwargs by name
Unnamed positional arguments will have names like
arg5
.- Call:
>>> kw = parser.get_argdict()
- Outputs:
- kw:
dict
Dictionary of positional and keyword names and values
- kw:
- get_argtuple() tuple ¶
Get list of all args and kwargs by name
Unnamed positional arguments will have names like
arg5
.
- get_cli_kwargs() dict ¶
Get full list of kwargs, including repeated values
- Call:
>>> args, kwargs = parser.get_args()
- Outputs:
- kwargs:
dict
Dictionary of named options and their values
- kwargs:
- classmethod get_optconverter(opt: str)¶
Get option value converter, if any, for option opt
Output must be a callable function that takes one argument
- classmethod get_optlist() set ¶
Get list of allowed options from cls and its bases
This combines the
_optlist
attribute from cls and any bases it might have that are also subclasses ofArgReader
.If optlist is an empty set, then no constraints are applied to option names.
- classmethod get_opttype(opt: str)¶
Get the type(s) allowed for the value of option opt
If opttype is
None
, no constraints are placed on the value of opt. The “value” differs from the “raw value” in that the “value” is after any converters have been applied.
- classmethod get_optvalmap(opt: str)¶
Get option value aliases, if any, for option opt
Output must be a
dict
- classmethod get_optvals(opt: str)¶
Get a set/list/tuple of allowed values for option opt
If optvals is not
None
, the full (post-optconverter) value will be checked if it isin
optvals.
- classmethod get_rawopttype(opt: str)¶
Get the type(s) allowed for the raw value of option opt
If opttype is
None
, no constraints are placed on the raw value of opt. The “raw value” is the value for opt before any converters have been applied.
- classmethod getx_cls_arg(attr: str, argname: str, vdef: Any | None = None) Any ¶
Get
dict
class attribute for positional parameterIf argname is
None
, the parameter (arg) has no name, and only"_arg_default_"
and"_default_"
can be used fromgetattr(cls, attr)
.Otherwise, this will look in the bases of cls if
getattr(cls, attr)
does not have argname. If cls is a subclass of anotherArgReader
class, it will search through the bases of cls until the first time it finds a class attribute attr that is adict
containing key.- Call:
>>> v = cls.getx_cls_key(attr, key, vdef=None)
- Inputs:
- Outputs:
- v:
None
|ojbect
Any value,
None
if not found
- v:
- classmethod getx_cls_dict(attr: str) dict ¶
Get combined
dict
for cls and its basesThis allows a subclass of
ArgReader
to only add to the_opttypes
or_optmap
attribute rather than manually include contents of all the bases.
- classmethod getx_cls_key(attr: str, key: str, vdef: Any | None = None) Any ¶
Access key from a
dict
class attributeThis will look in the bases of cls if
getattr(cls, attr)
does not have key. If cls is a subclass of anotherArgReader
class, it will search through the bases of cls until the first time it finds a class attribute attr that is adict
containing key.- Call:
>>> v = cls.getx_cls_key(attr, key, vdef=None)
- Inputs:
- Outputs:
- v:
None
|ojbect
Any value,
None
if not found
- v:
- classmethod getx_cls_set(attr: str) set ¶
Get combined
set
for cls and its basesThis allows a subclass of
ArgReader
to only add to the_optlist
attribute rather than manually include the_optlist
of all the bases.
- help_frontdesk(cmdname: str | None, opt: str = 'help') bool ¶
Display help message for front-desk parser, if appropriate
- infer_cmdname() str | None ¶
Infer sub-command if not determined by first argument
This command is usually overwritten in subclasses for special command-line interfaces where the primary task is inferred from options. This version always returns
None
- init_post()¶
Custom post-initialization hook
This function is called in the standard
__init__()
. The defaultinit_post()
does nothing. Users may define custom actions ininit_post()
in subclasses to make certain changes at the end of parsing- Call:
>>> opts.init_post()
- Inputs:
- opts:
ArgReader
Keyword argument parser instance
- opts:
- param_sequence¶
list
[str
,object
] – List of option name and value as parsed (includes duplicates in their original order)
- parse_cli_full(argv: list | None = None) ArgTuple ¶
Parse CLI args
- Call:
>>> a, kw = parser.parse_cli_full(argv=None)
- Inputs:
- Outputs:
- save_arg(arg: Any)¶
Save a positional argument
- single_dash_lastkey = False¶
Option to interpret multi-char words with a single dash the way POSIX command
tar
does, e.g.-cvf val
becomes{"c": True, "v": True, "f": "val"}
- single_dash_split = False¶
Option to interpret multi-char words with a single dash as single-letter boolean options, e.g.
-lh
becomes{"l": True, "h": True}
- validate_argval(j: int, argname: str, rawval: Any)¶
Validate a raw positional parameter (arg) value
- Call:
>>> val = opts.validate_argval(j, argname, rawval)
- Inputs:
- Outputs:
- val:
object
Converted value, either rawval or
optconverter(rawval)
- val:
- validate_opt(rawopt: str, rawval: Any) OptPair ¶
Validate a raw option name and raw value
Replaces rawopt with non-aliased name and applies any optconverter to rawval. Raises an exception if option name, type, or value does not match expectations.
- Call:
>>> optpair = opts.validate_opt(rawopt, rawval) >>> opt, val = opts.validate_opt(rawopt, rawval)
- Inputs:
- Outputs:
- cape.argread.BOOL_TYPES = (<class 'bool'>, <class 'numpy.bool'>)¶
Collection of boolean-like types:
bool
|numpy.bool_
- cape.argread.FLOAT_TYPES = (<class 'float'>, <class 'numpy.floating'>)¶
Collection of floating-point types:
float
|numpy.float16
|numpy.float32
|numpy.float64
|numpy.float128
- class cape.argread.FlagsArgReader(*args, **kw)¶
Subclass of
ArgReader
forreadflags()
The class attribute
FlagsArgRead.single_dash_split
is set toTrue
so that-opt val
is interpreted as{"o": True, "p": True, "t": True}
and
"val"
becomes an argument.- single_dash_split = True¶
Option to interpret multi-char words with a single dash as single-letter boolean options, e.g.
-lh
becomes{"l": True, "h": True}
- cape.argread.INT_TYPES = (<class 'int'>, <class 'numpy.integer'>)¶
Collection of integer (including unsigned) types:
int
|numpy.int8
|numpy.int16
|numpy.int32
|numpy.int64
|numpy.uint8
|numpy.uint16
|numpy.uint32
|numpy.uint64
- class cape.argread.KeysArgReader(*args, **kw)¶
Subclass of
ArgReader
forreadkeys()
The class attribute
KeysArgRead.single_dash_split
is set toFalse
so that-opt val
is interpreted as{"opt": "val"}
- single_dash_split = False¶
Option to interpret multi-char words with a single dash as single-letter boolean options, e.g.
-lh
becomes{"l": True, "h": True}
- class cape.argread.MetaArgReader(name: str, bases: tuple, namespace: dict)¶
Metaclass for
ArgReader
This metaclass combines attributes w/ bases. For example if creating a new class
Class2
that inherits fromClass1
, this will automatically combineClass1._optlist` and ``Class2._optlist
and save the result asClass2._optlist
. This happens behind the scenes so that users do not need to worry about repeating_optlist
entries.- classmethod combine_attrs(clsj: type, cls: type)¶
Combine attributes of clsj and cls
- Call:
>>> metacls.combine_attrs(clsj, cls)
- Inputs:
- metacls:
type
The
MetaArgReader
metaclass- clsj:
type
Parent class (basis) to combine into cls
- cls:
type
New class in which to save combined attributes
- metacls:
- classmethod combine_dict(clsj: type, cls: type, attr: str)¶
Combine one dict-like class attribute of clsj and cls
- Call:
>>> metacls.combine_dict(clsj, cls, attr)
- Inputs:
- metacls:
type
The
MetaArgReader
metaclass- clsj:
type
Parent class (basis) to combine into cls
- cls:
type
New class in which to save combined attributes
- attr:
str
Name of attribute to combine
- metacls:
- classmethod combine_tuple(clsj: type, cls: type, attr: str)¶
Combine one tuple-like class attribute of clsj and cls
- Call:
>>> metacls.combine_tuple(clsj, cls, attr)
- Inputs:
- metacls:
type
The
MetaArgReader
metaclass- clsj:
type
Parent class (basis) to combine into cls
- cls:
type
New class in which to save combined attributes
- attr:
str
Name of attribute to combine
- metacls:
- cape.argread.OPTLIST_TYPES = (<class 'set'>, <class 'tuple'>, <class 'frozenset'>, <class 'list'>)¶
Acceptable types for
ArgReader._optlist
- class cape.argread.OptPair(opt, val)¶
Option name/value pair
- opt¶
Alias for field number 0
- val¶
Alias for field number 1
- cape.argread.STR_TYPES = (<class 'str'>, <class 'numpy.str_'>)¶
Collection of string-like types:
str
|numpy.str_
- class cape.argread.SubCmdTuple(cmdname, argv)¶
- argv¶
Alias for field number 1
- cmdname¶
Alias for field number 0
- class cape.argread.SubParserCheck(cmdname, subparser, ierr)¶
- cmdname¶
Alias for field number 0
- ierr¶
Alias for field number 2
- subparser¶
Alias for field number 1
- class cape.argread.SubParserTuple(cmdname, subparser)¶
- cmdname¶
Alias for field number 0
- subparser¶
Alias for field number 1
- class cape.argread.TarFlagsArgReader(*args, **kw)¶
Subclass of
ArgReader
forreadflags()
The class attributes are
TarArgRead.single_dash_split = True TarArgRead.single_dash_lastkey = True
so that
-opt val
is interpreted as{"o": True, "p": True, "t": "val"}
- single_dash_lastkey = True¶
Option to interpret multi-char words with a single dash the way POSIX command
tar
does, e.g.-cvf val
becomes{"c": True, "v": True, "f": "val"}
- single_dash_split = True¶
Option to interpret multi-char words with a single dash as single-letter boolean options, e.g.
-lh
becomes{"l": True, "h": True}
- cape.argread.randomstr(n: int = 15) str ¶
Create a random string encded as a hexadecimal integer
- Call:
>>> txt = randomstr(n=15)
- Inputs:
- n: {
15
} |int
Number of random bytes to encode
- n: {
- cape.argread.readflags(argv=None)¶
Parse args where
-cj
becomesc=True, j=True
- cape.argread.readflagstar(argv=None)¶
Parse args where
-cf a
becomesc=True, f="a"
- cape.argread.readkeys(argv=None)¶
Parse args where
-cj
becomescj=True