DecreeTree Class API#
The decree_tree.DecreeTree
class is the basic building
block for nested commands.
Basics#
The DecreeTree
has several instance
and class variables. To instantiate a new
DecreeTree
, use
DecreeTree(...)
as normal.
All arguments are required to be passed by keyword.
- class decree_tree.DecreeTree(*, name=None, inherit=True, prog_is_name=False, version=None)#
Bases:
Tree
[DecreeTree
],Decree
A class for creating a command-line program using
argparse
subcommands.Initialize the variables specific to the combined DecreeTree. This method exists to make its interface explicit, rather than relying on unspecified
**kwargs
.- Parameters:
name (
str
|None
) – the command name, if overriding it for the instanceinherit (
bool
) – whether parents are considered when invoking a DecreeTreeprog_is_name (
bool
) – whether to useself.name
as the program name in helpversion (
str
|None
) – version information; use--version
to show
- help: ClassVar[str] = ''#
short help text to use (allows __doc__ to be lengthy)
- name: str = ''#
the name of the command, if not snake-cased class name
This attribute has a default value at the class level but can be overridden on a per-instance basis.
- inherit#
whether parents are considered when invoking a DecreeTree
- options: argparse.Namespace#
the set of argparse-processed options to be used
This attribute itself is not normally set by end user code, but items in this namespace may be created or read.
- prog_is_name: bool#
whether to use the
name
as theprog
, for help
- version: str | None#
version information; use
--version
to showThis attribute could be set at the top level of an inherited tree.
Critical Methods#
These are the subset of DecreeTree
methods that are most typically
called, or overridden in subclasses.
- DecreeTree.add_arguments(parser)#
Add arguments to
parser
(notself._decree_parser
), if any, optionally adding arguments “inherited” from the parentDecreeTree
. Subclass overrides typically include a call tosuper().add_arguments(parser)
. Handle manual argument processing inprocess_options
, not here.- Parameters:
parser (
ArgumentParser
) – the parser to which arguments will be added- Return type:
None
This command is typically not called by end-user code. To use it to add arguments to a command parser, override it in a subclass:
from decree_tree import DecreeTree class MyCommand(DecreeTree): def add_arguments(self, parser): super().add_arguments(parser) parser.add_argument('-o', '--option', action='store_true', help='a Boolean option') parser.add_argument('input', help='a positional argument')
See the arparse docs for details on the sorts of options, arguments, and groups that can be added to the parser. Note that typically there is no need to add subparsers here, as nested DecreeTrees fill this role.
- DecreeTree.process_options()#
Perform any needed processing on the options, prior to execution, optionally processing arguments “inherited” from the parent
DecreeTree
. Subclass overrides typically include a call tosuper().process_options()
.- Return type:
None
This command is typically not called by end-user code.
- DecreeTree.execute()#
Execute [sub]command processing, optionally executing processing “inherited” from the parent
DecreeTree
. Subclass overrides typically include a call tosuper().execute()
.- Returns:
Any
– any required data
This command is typically not called by end-user code. To use it to define the code to be ultimately executed by a command, override it in a subclass:
from decree_tree import DecreeTree class MyCommand(DecreeTree): def execute(self): data = super().execute(parser) if self.options.print: print(data)
Use the processed command-line arguments stored in
self.options
.
- DecreeTree.run(argv=None, options=None)#
Run the command via command line or with defined arguments. The command object executing
run()
will be considered the to be the root of the command, aside from any inherited processing. To override the actual command line arguments, pass toargv
either a space-delimited string of tokens or a sequence of individual token strings.- Parameters:
argv (
Sequence
[str
] |None
) – command-line arguments to parseoptions (
Namespace
|None
) – processed arguments, circumventing parse_args if specified
- Raises:
Exception – if any are raised within the running DecreeTree
- Returns:
Any
– the results from execution, if any
This command is the starting point for building the tree of command parsers, processing the options and arguments observed on the command line, and executing the selected commands. It is typically not overridden in end-user code. For simple standalone scripts,
run()
may be called from within a typical check for the module name:if __name__ == '__main__': MyRootCommand().run()
Sometimes
run()
needs to be invoked in a way that doesn’t use the actual command-line tokens. This is often be the case during testing. To provide a different set of command line tokens, pass theargv
argument torun()
:# split() will be called on the string MyRootCommand().run('--option value') # or split it manually MyRootCommand().run(['--option', 'value'])
Similarly, to inject processed option and argument values as an
argparse.Namespace
(whenrun
callsparser.parse_args()
fromargparse
), use theoptions
argument.
Child Command Manipulation#
The class has several ways to manipulate its list of child commands.
For this section, assume that Root
, Foo
, Bar
, Baz
,
and Qux
are DecreeTree
subclasses, all imported from a module
named nesting
.
Refer to Nesting Commands for an overview of how to nest DecreeTree
commands.
- DecreeTree.add(child)#
A shortcut to append a new child, equivalent to
append_child
.- Parameters:
child (
DecreeTree
|Type
[DecreeTree
]) – the DecreeTree object, class, or subclass to append- Returns:
DecreeTree
– the child itself, in case it was created within this method
Augment a tree by adding a child command to a parent. The new child will be appended to the list of children at that level. If this method (and other similar methods) receives a
DecreeTree
class, it will instantiate it. Executing this…root = Root() foo = root.add(Foo) bar = root.add(Bar(version='1.0.0')) baz = Baz() foo.add(baz) qux = foo.add(Qux()) print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> foo -> baz: nesting.Baz root -> foo -> qux: nesting.Qux root -> bar: nesting.Bar
Note that this method can be equivalently invoked as
append_child
.
- DecreeTree.child_index(name, raise_exception=False)#
Determine the index of a child.
- Parameters:
name (
str
) – the name of the child to look upraise_exception (
bool
) – whether to raise an exception if the child cannot be found
- Raises:
InvalidName – if the child cannot be found and raise_exception is True
- Returns:
int
|None
– the index of the child or None if not found
Determine the list index of a particular child command in order to manipulate its containing list. Executing this…
root = Root() foo = root.add(Foo) bar = root.add(Bar) i = root.child_index('bar') print(f'{i=}')
... leads to this output:
i=1
- DecreeTree.clear_children()#
Clear the list of children.
- Return type:
None
Empty out the list of children for the
DecreeTree
. Executing this…root = Root() foo = root.add(Foo) bar = root.add(Bar) print(root.structure) print('...') root.clear_children() print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> bar: nesting.Bar ... root: nesting.Root
- DecreeTree.find_child(name, raise_exception=False)#
Find a direct child by name and return it or None.
- Parameters:
name (
str
) – the name of the child to findraise_exception (
bool
) – whether to raise an exception if the child cannot be found
- Raises:
InvalidName – if the child cannot be found and raise_exception is True
- Returns:
DecreeTree
|None
– the child or None if not found
This method is similar to
get
below, but can only retrieve a direct child of a singleDecreeTree
. For example:root = Root() foo_1 = root.add(Foo) foo_2 = root.find_child('foo') assert foo_2 is foo_1
Pass
raise_exception
if the method should raise an exception if a child with the particular name cannot be found, otherwiseNone
will be returned in that circumstance.
- DecreeTree.get(*args)#
Look up and provide the nested child object, equivalent to
get_child
.- Parameters:
*args (
str
) – the nested path of names to the requested object- Raises:
InvalidName – if a matching object cannot be found
- Returns:
DecreeTree
– the object found
Retrieve a child from an arbitrary depth in a command tree.
root = Root() foo = root.add(Foo) bar_1 = foo.add(Bar) bar_2 = root.get('foo', 'bar') assert bar_2 is bar_1
Note that this method can be equivalently invoked as
get_child
.
- DecreeTree.insert_child(index, child)#
Insert a new child.
- Parameters:
index (
int
) – the position at which to insert the childchild (
DecreeTree
|Type
[DecreeTree
]) – the object, class, or subclass to insert
- Returns:
DecreeTree
– the child object
Insert a child command to parents. Executing this…
root = Root() root.add(Foo) root.add(Bar) print(root.structure) print('...') root.insert_child(1, Baz) print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> bar: nesting.Bar ... root: nesting.Root root -> foo: nesting.Foo root -> baz: nesting.Baz root -> bar: nesting.Bar
- DecreeTree.remove_child(name)#
Remove a child.
- Parameters:
name (
str
) – The name of the child to remove.- Return type:
None
Insert a child command to parents. Executing this…
root = Root() root.add(Foo) root.add(Bar) print(root.structure) print('...') root.remove_child('foo') print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> bar: nesting.Bar ... root: nesting.Root root -> bar: nesting.Bar
Note that the removed child, and its tree of children if any, may still be accessible through another Python reference. This method does not delete them.
- DecreeTree.replace_child(name, child, raise_exception=False)#
Replace a child.
- Parameters:
name (
str
) – the name of the child to replacechild (
DecreeTree
|Type
[DecreeTree
]) – the new object, class, or subclassraise_exception (
bool
) – the raise_exception argument to the index() call
- Returns:
DecreeTree
|None
– the instantiated child object
Replace a child command with another. Executing this…
root = Root() root.add(Foo) root.add(Bar) print(root.structure) print('...') baz = Baz() baz.add(Qux) root.replace_child('bar', baz) print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> bar: nesting.Bar ... root: nesting.Root root -> foo: nesting.Foo root -> baz: nesting.Baz root -> baz -> qux: nesting.Qux
- DecreeTree.sort_children(*, key=None, reverse=False)#
Alphabetically sort the list of subcommands in place.
- Parameters:
key (
Callable
[[DecreeTree
],SupportsLT
|SupportsGT
] |None
) – a single-argument function used to extract the comparison key for each childreverse (
bool
) – whether to sort in reverse
- Return type:
None
Sort the direct children of a
DecreeTree
. Grandchildren are retained unsorted. Executing this…root = Root() root.add(Foo) root.add(Bar) root.add(Qux) print(root.structure) print('...') root.sort_children() print(root.structure) print('...') root.sort_children( key=lambda child: child.get_name[2:], reverse=True, ) print(root.structure)
... leads to this output:
root: nesting.Root root -> foo: nesting.Foo root -> bar: nesting.Bar root -> qux: nesting.Qux ... root: nesting.Root root -> bar: nesting.Bar root -> foo: nesting.Foo root -> qux: nesting.Qux ... root: nesting.Root root -> qux: nesting.Qux root -> bar: nesting.Bar root -> foo: nesting.Foo
Properties#
- property DecreeTree.children: list[DecreeTree]#
Provide a copy of the child objects. This could be a generator, but since the list of children is expected to be short, there shouldn’t be a need.
- Returns:
the list of child instances
>>> root = Root() >>> _ = root.add(Foo) >>> _ = root.add(Bar) >>> root.children [nesting.Foo(), nesting.Bar()]
- property DecreeTree.child_names: list[str]#
Provide a list of the names of the child objects. This could be a generator, but since the list of children is expected to be short, there shouldn’t be a need.
- Returns:
the list of the names of the child instances
>>> root = Root() >>> _ = root.add(Foo) >>> _ = root.add(Bar) >>> root.child_names ['foo', 'bar']
- property DecreeTree.count_children: int#
Provide the number of children.
- Returns:
the number of children
>>> root = Root() >>> _ = root.add(Foo) >>> _ = root.add(Bar) >>> root.count_children 2
- property DecreeTree.get_name: str#
Provides the name of the instance, to facilitate separation of concerns via the use of Tree.
- Returns:
the name of the instance
>>> from decree_tree import DecreeTree >>> class Root(DecreeTree): pass >>> root = Root() >>> root.get_name 'root'
- property DecreeTree.parent: DecreeTree | None#
Return the parent of the object, if any.
- Returns:
the parent object or None
>>> root = Root() >>> foo = root.add(Foo) >>> foo.parent nesting.Root()
- property DecreeTree.parents: list[DecreeTree]#
Generate a list of the parents of this tree, in order. This could be a generator, but since the list of parents is expected to be short, there shouldn’t be a need.
- Returns:
the parents
>>> root = Root() >>> foo = root.add(Foo) >>> bar = foo.add(Foo) >>> bar.parents [nesting.Root(), nesting.Foo()]
- property DecreeTree.parent_names: list[str]#
Generate a list of the names of the parents of this tree, in order. This could be a generator, but since the list of parents is expected to be short, there shouldn’t be a need.
- Returns:
the names of the parents
>>> root = Root() >>> foo = root.add(Foo) >>> bar = foo.add(Foo) >>> bar.parent_names ['root', 'foo']
- property DecreeTree.structure: str#
Show the structure of the nested tree. Relies on str() including a list of parents.
- Returns:
the structure string
>>> root = Root() >>> foo = root.add(Foo) >>> bar = foo.add(Bar) >>> baz = foo.add(Baz) >>> for line in root.structure.splitlines(): ... print(line) root: nesting.Root root -> foo: nesting.Foo root -> foo -> bar: nesting.Bar root -> foo -> baz: nesting.Baz
Additional Methods#
These methods are typically not called by nor overridden in
DecreeTree
subclasses, but can be if custom behavior is
desired.
- DecreeTree.configure_parser(subparsers=None)#
Configure the parser for this object. Typically not called by nor overridden in end-user code.
- Parameters:
subparsers (
_SubParsersAction
|None
) – the subparsers object from a parent, if any- Raises:
ValueError – when the configuration of the name or prog is incorrect
- Return type:
None
- DecreeTree.configure_parser_tree(subparsers=None, parser=None)#
Configure the parser for this tree. Typically not called by nor overridden in end-user code.
- Parameters:
subparsers (
_SubParsersAction
|None
) – the subparsers object from a parent, if anyparser (
ArgumentParser
|None
) – the parser to which arguments will be added, if notself._decree_parser
- Return type:
None
- DecreeTree.handle_run_exception(exc)#
Handle general exceptions raised within the
run
method.- Parameters:
exc (
Exception
) – the exception that was raised- Return type:
None
Override this method to change the default error handling behavior.
- DecreeTree.parser_options()#
Add to and override options passed to the argparse parser.
- Returns:
dict
[str
,Any
] – the options to provide to the parser
Override this method to provide additional arguments to the argparse.ArgumentParser constructor for this
DecreeTree
, when used as the root command.
- DecreeTree.preprocess_options(argv=None, options=None)#
Populate
self.options
if it isn’t already. Typically not called by nor overridden in end-user code.- Parameters:
argv (
Sequence
[str
] |None
) – command-line arguments to parseoptions (
Namespace
|None
) – processed arguments, circumventing parse_args if specified
- Return type:
None
- DecreeTree.subparsers_options()#
Add to and override options passed to
add_subparsers
.- Returns:
dict
[str
,Any
] – the options to provide to the subparser creator
Override this method to provide additional arguments to the add_parser call for this
DecreeTree
, when used as a child command.