coma.config.cli¶
Utilities for overriding config attributes with command line arguments.
- class OverrideProtocol(*args, **kwargs)[source]¶
Protocol for the function signature of
coma.config.cli.Override.__call__(). To make use of other defaultcomacomponents, user-defined alternative implementation should adhere to this same protocol.Protocol:
Callable[[OverrideData, InstanceKey], None]
with
OverrideDataandInstanceKey
- class OverridePolicy(value)[source]¶
Policy for handling cases where one parameter will override another in a Callable’s signature. For example, suppose a function defined as:
def fn(x, **kwargs): ...
is invoked as:
kwargs = dict(x=1, y=2) fn(x=3, **kwargs)
How should
xbe treated?- SILENT_OVERRIDE¶
Silently override the parameter value. In the above example, the result is
x=1sincekwargs["x"]applies last.
- VERBOSE_OVERRIDE¶
Like
SILENT_OVERRIDE, but emit awarninglisting which parameter is overridden and what the old and new values are.
- SILENT_SKIP¶
Silently skip over any parameter whose value has already been assigned. In the above example, the result is
x=3sincekwargs["x"]is silently skipped.
- VERBOSE_SKIP¶
Like
SILENT_SKIP, but emit awarninglisting which parameter is being skipped and what the current and skipped values are.
- RAISE¶
Raise an error if an override is being attempted.
- class OverrideData(config_id: str, configs: dict[str, Config], instance_key: str | None, unknown_args: list[str])[source]¶
All relevant data for overriding a config instance.
- configs¶
All configs that may be override-relevant. Configs that are not corresponding to
config_idinform concerns of override exclusivity and uniqueness. SeeOverridefor details.- Type:
- instance_key¶
The specific instance of the
config_idconfig to override. IfNone, the latest is used instead. If notNone, the same key is used to probe the otherconfigsfor override exclusivity and uniqueness. SeeOverridefor details.- Type:
InstanceKey, optional
- unknown_args¶
The list of unknown command line arguments, some of which may specify overrides for this
config_idconfig. Typically, this is the second return value of parse_known_args().
- class Override(sep: str = '::', exclusive_prefixed: bool = True, exclusive_shared: bool = False, unique_overrides: bool = True)[source]¶
Attempts to override a config instance’s attributes with command line arguments.
- sep¶
The prefix separation string to use. Can be any string, though some options (such as
"=",":","{","}","[","]",",","'",'"',".","$", etc.) will likely cause parsing errors. Use these with caution.- Type:
Whether shared overrides should match at most one config.
- Type:
Similar to from_dotlist() followed by merge(), but with additional features and support for list-like configs. In particular,
omegaconfalways overrides list configs entirely when merging (discarding the original), whereas here we ensure top-level lists are extended instead. Non-top-level lists (for example, a list as one of the fields of a dict-like config) are treated as conventionalomegaconflist configs (override instead of merge).Specifically, since
comacommands accept an arbitrary number of configs, config attributes’ names may end up clashing when using pureomegaconfdot-list notation. To resolve these clashes, a prefix notation is introduced.Prefix Notation
For a config with identifier
config_id, anyomegaconfdot-list notation can be prefixed withconfig_idfollowed bysepto uniquely link the override to the corresponding config.In addition, an attempt is made to match all non-prefixed arguments in dot-list notation to the config corresponding to
config_id. These shared config overrides are not consumed, and so can be used to override multiple configs without duplication. However, this powerful feature can also be error-prone. To disable it, setexclusive_sharedtoTrue. This raises aValueErrorif shared overrides match more than one config.Note
If the config is not structured,
omegaconfwill happily add any attributes to it. To prevent this, ensure that the config is structured (by instantiating it from adataclassbackend type or by using structured() or set_struct() on adict-based config).Finally, prefixes can be shortened to any leading substring. For example,
'long'or even just'l'matches against the config identifier'long_config_id'. By default, prefixes have to be unambiguous (i.e., have to match against at most one config identifier). To disable this, setexclusive_prefixedtoFalse. Then, all matching configs to a given prefix will be overridden. Be cautious.To toggle whether each command line argument should itself be unique, set
unique_overridesaccordingly.Note
The uniqueness of command line arguments is based on their (non-prefixed) string value of the field key, not on their effects on any config object. For example,
"x=1"and"x=2"are not correctly determined as being not unique because"x" == "x", whereas"a.b=1"and"a[b]=2"will slip by the uniqueness detection because"a.b" != "a[b]"(as a string value).Note
Regardless of their ordering as command line arguments, all prefixed overrides are processed before all shared overrides. This is not a problem when
unique_overridesisFalse, but can lead to an unexpected outcome when it isTrue. For example,"x=1"followed by"prefix::x=2"will lead to a final value ofx == 1. To avoid this unexpected outcome, makes sure to place all prefixed command line arguments before all shared arguments, or disable shared arguments entirely by settingexclusive_sharedtoFalse.Examples
Resolving clashing dot-list notations with (abbreviated) prefixes:
@dataclass class Person: name: str @dataclass class School: name: str @coma.command def enroll_student(person: Person, school: School): ...
Invoking on the command line (assuming
sepis"::"):$ python main.py enroll_student p::name="..." s::name="..."
- class ParamData(configs: dict[str, Config] = <factory>, supplemental_configs: dict[str, Config] = <factory>, inline_identifier: str = 'inline', inline_config: ~coma.config.base.Config | None = None, other_parameters: dict[str, ~typing.Any] = <factory>, args_id: str | None = None, kwargs_id: str | None = None)[source]¶
Utilities for creating configs and other parameters from a Callable’s signature, manipulating these, and calling the Callable using the result.
- supplemental_configs¶
Any additional configs to manipulate that don’t appear in the Callable’s signature and won’t be called on it. Helpful for providing additional information in the manipulation process.
- Type:
- inline_config¶
The special config collecting all inline parameter from the Callable’s signature.
- Type:
Config, optional
- other_parameters¶
Every non-config parameter in the Callable’s signature.
- Type:
- args_id¶
The identifier of the variadic positional parameter (if any) of the Callable’s signature. Depending on the signature’s specifics, the associated data (if any) is either in
configsor inother_parameters.- Type:
Identifier, optional
- kwargs_id¶
The identifier of the variadic keyword parameter (if any) of the Callable’s signature. Depending on the signature’s specifics, the associated data (if any) is either in
configsor inother_parameters.- Type:
See also
from_signature():For specifics on how the above attributes are inferred from the Callable’s signature.
- get_inline_id() str[source]¶
Returns the identifier of the inline config (whether it exists or not).
- is_inline_id(config_id: str) bool[source]¶
Returns whether
config_idis the identifier of the inline config.
- get_all_configs(include_inline: bool = True) dict[str, Config][source]¶
Returns all
configsandsupplemental_configs. Ifinclude_inlineisTrueand an inline config exists, it is also returned.
- is_serializable(config_id: str) bool[source]¶
Returns whether
config_idcorresponds to a serializable config. All configs are serializable except for variadic positional (*args), variadic keyword (**kwargs), and the specialinline_config.
- select(*ids: str, default: ~typing.Any = <object object>) dict[str, Any][source]¶
Returns the objects associated with the selected
ids, keyed by identifier. These can be in any ofconfigs,supplemental_configs,other_parameters, orinline_config. Forinline_config, only the aggregate inline config can be retrieved, not the individual parameters that collectively make it up. To retrieve it, useget_inline_id().- Parameters:
*ids (
Identifier) – Collection of identifiers for which to select data.default (Any) – If given, the value for identifiers in
idswith no existing data is set to this value. If not given, raise aKeyErrorif data does not exist for at least one identifier.
- Returns:
A mapping between the selected identifiers and their associated value.
- Return type:
dict[
Identifier, typing.Any]- Raises:
KeyError – If
defaultis not specified, and data does not exist for at least one identifier inids.
- select_config(*config_ids: str, default: ~typing.Any = <object object>) dict[str, Config][source]¶
Returns the configs associated with the selected
config_ids, keyed by identifier. These can be in any ofconfigs,supplemental_configs, orinline_config. Forinline_config, only the aggregate inline config can be retrieved, not the individual parameters that collectively make it up. To retrieve it, useget_inline_id().- Parameters:
- Returns:
A mapping between the selected config identifiers and their associated configs.
- Return type:
- Raises:
KeyError – If
defaultis not specified, and a config does not exist for at least one config identifier inconfig_ids; or if any identifier inconfig_idsrefers to a parameter instead of a config.
- get(identifier: str, default: ~typing.Any = <object object>) Any[source]¶
Returns the object associated with
identifier. This object can be in any ofconfigs,supplemental_configs,other_parameters, orinline_config. Forinline_config, only the aggregate inline config can be retrieved, not the individual parameters that collectively make it up. To retrieve it, useget_inline_id().- Parameters:
identifier (
Identifier) – The identifier for which to retrieve data.default (Any) – If given, returns this value if no data exists for
identifier. If not given, raise aKeyErroron missing data.
- Returns:
The data associated with
identifier, ordefaultif no such data exists anddefaultis given.- Return type:
- Raises:
KeyError – If
defaultis not given, and data foridentifierdoes not exist.
- get_config(config_id: str, default: ~typing.Any = <object object>) Config[source]¶
Returns the config associated with
config_id. The config can be in any ofconfigs,supplemental_configs, orinline_config. Forinline_config, only the aggregate inline config can be retrieved, not the individual parameters that collectively make it up. To retrieve it, useget_inline_id().- Parameters:
- Returns:
The config associated with
config_id, ordefaultif no such config exists anddefaultis given.- Return type:
- Raises:
KeyError – If
defaultis not given, and a config forconfig_iddoes not exist; or ifconfig_idrefers to a parameter instead of a config.
- replace(identifier: str, new_value: Any) None[source]¶
Replaces the data associated with
identifier. This object can be in any ofconfigs,supplemental_configs, orother_parameters, orinline_config. Forinline_config, only the aggregate inline config can be replaced (as a whole), not the individual parameters that collectively make it up. Useget_inline_id().- Parameters:
identifier (
Identifier) – The identifier for which to replace the data.new_value (Any) – The new value.
- Raises:
KeyError – If data for
identifierdoes not already exist. To add new data for a new identifier, add an entry directly to the desired attribute dictionary.
- delete(*ids: str, raise_on_missing: bool = True) None[source]¶
Deletes the data associated with each identifier in
ids. These can be in any ofconfigs,supplemental_configs, orother_parameters, orinline_config. Forinline_config, only the aggregate inline config can be deleted, not the individual parameters that collectively make it up. To delete it, useget_inline_id().- Parameters:
*ids (
Identifier) – Collection of identifiers for which to delete data.raise_on_missing (bool) – If
True, raise aKeyErrorif data does not already exist for at least one identifier inids. IfFalse, silently ignore missing identifiers.
- Raises:
KeyError – If
raise_on_missingisTrue, and data does not already exist for at least one identifier inids.
- classmethod from_signature(signature: Signature, *, args_as_config: bool, kwargs_as_config: bool, inline_identifier: str, inline: Sequence[str | tuple[str, Callable[[], Any]]], supplemental_configs: dict[str, Any]) ParamData[source]¶
Returns a
ParamDatafilled according to the specifics ofsignatureand the various additional criteria. Allsupplemental_configsare invariably treated as configs and converted intoConfigswithout additional checks besides ensuring that the identifiers of supplemental configs do not clash with any identifiers insignatureor withinline_identifier.The distinction between
configsandother_parametersinsignatureis determined by inspecting its type annotation (if any), its default value (if any), its kind, and whether the parameter identifier is marked asinline.An inline parameter is a one-off config field. Specifically, all
inlineparameters are aggregated into a specialinline_config, which is backed by a programmaticdataclass. This provides all the rigorous runtime type validation of a standarddataclass-backedomegaconfconfig without requiring adataclassto be created just for those one-off fields. Moreover, inline configs are considered non-serializable by default.configstake priority overother_parameters: If a parameter can be considered a config (as per the criteria below), it is treated as one. A non-config parameter is assumed to be regular parameters unless it meets the inline criteria (below) in which case it is treated as inline.Criteria for interpreting a parameter as a config:
1. The parameter has a type annotation that exactly matches one of
list,dict, or any dataclass type. We refer to these as “config annotations”.2. The parameter does not have a default value. Since configs employ a dedicated initialization protocol, default parameter values are not needed.
Note
This means that a convenient way to ensure that a config-annotated parameter is interpreted as a regular parameter is to give it a default. For example,
list_cfg: listis interpreted as a config whereasnon_cfg_list: list = Noneis interpreted as a regular parameter.3. The parameter’s identifier in not found in
inline. Even if the parameter has a config annotation, being ininlinedisqualifies.4. Special case: Because variadic positional (
*args) and variadic keyword (**kwargs) parameters cannot be assigned defaults in Python, and because they can never be marked asinline, criteria (2) and (3) cannot be used. Instead, use the flagsargs_as_configandkwargs_as_config.Checklist for interpreting a parameter as inline:
The parameter has a type annotation. A missing annotation is disqualifying.
The parameter has a default value. A missing default value is disqualifying.
Note
On mutable inline default values. Because it is un-Pythonic to declare a mutable default value in a function definition, it can be tricky to set a good default value for inline parameters. So, items in
inlinecan consist of either just aParamIDs, or be 2-tuple where the first value is aParamIDand the second value is adefault_factoryconforming to the requirements of the same argument in dataclasses.field(). It is an error to give both a signature-level default and an inline-level default factory.3. The default value is a valid instance of the annotation type. If not, the underlying
omegaconfcall will raise aValidationError.4. The parameter’s identifier is found in
inline. If this is true, but one of the above criteria are violated, an error is raised.5. The parameter’s kind is not variadic positional or variadic keyword. These two special cases can be configs or regular parameters, but never inline.
Example
Even though
Databelow is adataclass, it is not considered a config because of its non-config annotation and itsNonedefault value (either one of which is disqualifying on its own). On the other hand,out_filecan be overridden on the command line because of its inline declaration. Any list-like command line arguments are not fed to*argsbecauseargs_as_configisFalsewhereas the opposite is true for**kwargsand dict-like command line arguments becausekwargs_as_configisTrue(by default).@dataclass class Data: x: int = 42 @dataclass class Config: y: float = 3.14 @coma.command( signature_inspector=SignatureInspector( args_as_config=False, inline=["out_file"], ), ) def cmd( cfg: Config, data: Optional[Data] = None, out_file: str = "out.txt", *args, **kwargs, ): print("cfg is:", cfg) print("data is:", data or Data()) print("out_file is:", out_file) print("*args is:", args) print("*kwargs is:", kwargs)
Invoking on the command line with some overrides results in the following:
$ python main.py cmd x=1 y=2 z inline::out_file=foo.txt cfg is: Config(y=2.0) data is: Data(x=42) out_file is: foo.txt *args is: () *kwargs is: {'x': 1, 'y': 2}
In the example above, notice that:
1. The list-like argument
'z'is not in*argsbecause*argsis not a config.2.
**kwargsincludes both dict-like arguments.3.
out_fileis overridden.4.
out_fileis prefixed with the reserved config identifier"inline"to prevent**kwargsfrom also containing an"out_file"entry. This prevents a runtime error resulting from"out_file"appearing multiple times in the Callable’s parameter list.5. Because
cfgis a config, it’syattribute was also overridden (this is the default override model where overrides are applied as widely as possible; to disable, seeOverride).6. Because
datais not a config, it’sxattribute is not overridden. In fact, because the default value ofdatais not replaced in anycommand()hook, its value when invoking this command will invariably beNone. Usereplace()in a hook to change this.- Parameters:
signature (inspect.Signature) – The signature of the Callable from which to create and fill a
ParamData.args_as_config (bool) – Whether to treat the variadic positional parameter in
signature(if any) as a list config or as a regular parameter.kwargs_as_config (bool) – Whether to treat the variadic keyword parameter in
signature(if any) as a dict config or as a regular parameter.inline_identifier (
ConfigID) – The config identifier to use for the inline config.inline (Sequence) – The parameters in
signatureto mark as inline config parameters (if any). Items in this sequence must either beParamIDs or be 2-tuple where the first value is aParamIDand the second value is adefault_factoryconforming to the requirements of dataclasses.field().supplemental_configs (
Parameters) – Any additional parameters not insignatureto convert into configs.
- Returns:
Filled according to the specifics of
signatureand the various allowance criteria.- Return type:
- Raises:
ValueError – If any parameter identifier in
supplemental_configsmatches an existing parameter insignature; or if any parameter identifier or supplemental config identifier is the (case-insensitive)inline_identifier; or if any parameter is misspecified for its type (e.g., missing a default value on an inline parameter).
- call_on(fn: Callable[[...], T], policy: OverridePolicy, instance_key: str | None = None) T[source]¶
Calls
fnusing the current state ofconfigsandother_parameters, returning the return value offn.- Parameters:
fn (Callable) – The Callable to call using internal signature data.
policy (
OverridePolicy) – Policy for when some keyword-based parameter would override another parameter.instance_key (
InstanceKey, optional) – The specific instance of the variousself.configsto pass tofn. IfNone, the latest is used instead. If notNone, the same key is used for allself.configsand must exist for all of them.
- Returns:
The return value from calling
fn.- Raises:
ValueError – If one of the parameters in the signature of
fncannot be filled by internal data; or if at least one of the configs was never instantiated; or ifpolicycauses a raise on a parameter.KeyError – If
instance_keyis not a valid key for at least one config.Others – As may be raised by the underlying
omegaconfimplementation of the configs.
- class SignatureInspectorProtocol(*args, **kwargs)[source]¶
Protocol for a command signature inspector that takes in a command signature object and supplemental configs and returns a (possible subclassed) instance of
ParamData.To make use of other default
comacomponents, user-defined alternative implementation should adhere to this same protocol and to the protocols (all public methods exceptfrom_signature()) ofParamData.Protocol:
Callable[[inspect.Signature, dict[ConfigID, Any]], ParamData]
- class SignatureInspector(args_as_config: bool = True, kwargs_as_config: bool = True, inline_identifier: str = 'inline', inline: Sequence[str | tuple[str, Callable[[], Any]]] = ())[source]¶
Lightweight wrapper around
from_signature()acting as the defaultSignatureInspectorProtocolwhen no user-defined alternative is provided.- args_as_config¶
Whether to treat the variadic positional parameter in the command signature (if any) as a list config or a regular parameter.
- Type:
- kwargs_as_config¶
Whether to treat the variadic keyword parameter in the command signature (if any) as a dict config or a regular parameter.
- Type:
- inline_identifier¶
The config identifier to use for the inline config (regardless of whether there is one).
- Type:
- inline¶
The parameters in the command signature to mark as inline config parameters (if any).
See also