soleil.cli_tools.solconfarg#
Classes
|
Enables adding soleil configured objects as arguments in argparse CLIs. |
- class soleil.cli_tools.solconfarg.SolConfArg(config_source: Path | str | None = None, resolve=True, **load_kwargs)#
Bases:
objectEnables adding soleil configured objects as arguments in argparse CLIs.
- Parameters:
config_source – The path to the configuration file to load. If not specified, will be required as a CLI argument.
resolve – Whether
ArgumentParser.parse_args(equivalently,__call__()) returns the resolved or unresolved object.load_kwargs – Any extra keyword arguments to pass internally to
load_solconf().
Instances of this object can be passed as a value for the
typekeyword argument when callingargparse.ArgumentParser.add_argument()(see the argparse documentation).Assuming no overrides are specified, doing so will set the parsed value of that argument to the resolved object loaded from the
config_sourceinitialization argument:>>> from argparse import ArgumentParser >>> from soleil.cli_tools import SolConfArg >>> parser = ArgumentParser() >>> parser.add_argument('my_obj', type=SolConfArg(soleil_examples / 'vanilla/main.solconf')) ReduceAction(...) >>> parser.parse_args([]) Namespace(my_obj={'a': 1, 'b': 2, 'c': 3})
Note
Monkey-patching of argparse
SolConfArgmonkey-patches the built-in argparse module in order to support having aSolConfArg-typed argument consume multiple command line overrides and reduce them to a single parsed element (as opposed to a list with one entry per override).The applied patch should not affect normal operation of argparse and is only applied once module
soleil.cli_tools(or one of its member) is imported.The source code for theses patches is in module
soleil.cli_tools._argparse_patches.Under the hood
Using a
SolConfArginstance as the type of a parser argument implicitly sets the default value of thenargsandactionarguments:parser = ArgumentParser() parser.add_argument( ... type=SolConfArg(...), action=<defaults to ReduceAction()>, nargs=<defaults to '+' or '*'>, ... ) parser.parse_args([...])
The
actionkeyword is set to an instance ofReduceAction()that is responsible for gathering all the CLI arguments corresponding to theSolConfArg()parser entry as overrides and resolving the described object with these overrides included by means of a call toSolConfArg.__call__().Accordingly, the object returned for the argument added in the code above can also be obtained as follows:
sc = SolConfArg(...) sc([...])
Note
For conciseness, we use the above syntax in examples below.
Number of consumed CLI arguments
If
config_sourceis not provided atSolConfArginitialization, the first CLI argument will be used in its place. Accordingly CLI arguments of typeSolConfArgwill by default consumeone-or-more CLI arguments when
config_sourceis not provided, orzero-or-more CLI arguments when
config_sourceis provided.
Any extra CLI arguments besides
config_sourceare treated as CLI overrides.This behavior is implemented by internally setting the default value of keyword argument
nargsusingnargs='+'ornargs='*', respectively,
in the
argparse.ArgumentParser.add_argument()call. Users can change this behavior, e.g., by usingnargs=1ornargs=0, respectively,
in effect disabling CLI overrides.
Note
An argparse argument of type
SolConfArgwith the defaultnargs='+'ornargs='*'will consume an unbounded number of CLI arguments. Such arguments must either be the last non-optional argument added to theArgumentParserobject, or an optional argument.Note
Even when
SolConfArgis instantiated with aconfig_sourceargument, the value ofconfig_sourcecan be overriden from the command line using a source clobber override.# Option 1: Path must be provided with argparse arguments >>> sca1 = SolConfArg() >>> sca1([soleil_examples/'vanilla/main.solconf']) {'a': 1, 'b': 2, 'c': 3} # Option 2: Path provided with argument definition >>> sca2 = SolConfArg(soleil_examples/'vanilla/main.solconf') >>> sca2(["a=10", "c=30"]) {'a': 10, 'b': 2, 'c': 30}
Source clobber
In the case where a path is specified in the initializer, it can still be overriden using a source clobber override:
# Source clobber assignment must be the first argument in the overrides list >>> sca2 = SolConfArg(soleil_examples/'vanilla/main.solconf') >>> sca2([f"**={soleil_examples/'vanilla/nested.solconf'}"]) {'letters': {'a': 1, 'b': 2, 'c': 3}}
Note that the source clobber override must be the first item in the overrides list.
Deeper overrides
The target of an override provided to the left of the override assignment operator can consist of any valid variable name path:
>>> sca = SolConfArg(soleil_examples/'vanilla/nested.solconf') >>> sca() ## {'letters': {'a': 1, 'b': 2, 'c': 3}} >>> sca(['letters.b=20']) {'letters': {'a': 1, 'b': 20, 'c': 3}}
- property DFLT_ARGPARSE_KWARGS#
Sets the default
argparser.add_argumentkeyword argument values to use when this instance is used as thetypekeyword argument.
- __call__(overrides: List[str] | None = None)#
Resolves the argument, applying all input overrides.
- get_config_source()#
Returns the config source path, which will depend on whether a config source was specified explicitly at initialization or not, and whether a source clobber override was specified.