cmdkit.config
#
Classes and interfaces for managing application level configuration parameters. Get a runtime configuration with a namespace-like interface from both local files and environment variables with appropriate precedent.
Reference#
- class Configuration(**namespaces: Namespace)[source]#
Bases:
NSCoreMixin
An ordered collection of Namespace dictionaries. The update behavior of
Namespace
is used to provide a layering effect for configuration parameters.- Example:
>>> conf = Configuration(one=Namespace({'x': 1, 'y': 2}), ... two=Namespace({'x': 3, 'z': 4})
>>> conf Configuration(one=Namespace({'x': 1, 'y': 2}), two=Namespace({'x': 3, 'z': 4}))
>>> conf.x, conf.y, conf.z (3, 2, 4)
>>> conf.namespaces.keys() dict_keys(['one', 'two'])
>>> conf.namespaces.one.x 1
- extend(**others: Namespace | Environ) None [source]#
Extend the configuration by adding namespaces.
- Example:
>>> conf = Configuration(one=Namespace({'x': 1, 'y': 2}), ... two=Namespace({'x': 3, 'z': 4}) >>> conf.extend(three=Namespace({'y': 5, 'u': {'i': 6, 'j': 7}})) >>> conf Configuration(one=Namespace({'x': 1, 'y': 2}), two=Namespace({'x': 3, 'z': 4}), three=Namespace({'y': 5, 'u': {'i': 6, 'j': 7}})
- classmethod from_local(*, env: bool = False, prefix: str | None = None, default: dict | None = None, **files: str) Configuration [source]#
Create configuration from a cascade of files. Optionally include env.
- Example:
>>> import os >>> HOME, CWD = os.getenv('HOME'), os.getcwd() >>> conf = Configuration.from_local(default=Namespace(), ... env=True, prefix='MYAPP', ... system='/etc/myapp.yml', ... user=f'{HOME}/.myapp.yml', ... local=f'{CWD}/.myapp.yml')
- classmethod from_context(name: str, create_dirs: bool = True, config_format: str = 'toml', default_config: dict | None = None) Tuple[AppContext, Configuration] [source]#
Build default configuration from application context.
This builder method is shorthand for calling
default()
onAppContext
and passing it intoConfiguration.from_local()
with environment variables enabled and system, user, and local passed accordingly.- Example:
>>> default_cfg = Namespace(logging={'level': 'info'}, server={'port': 54321}) >>> ctx, cfg = Configuration.from_context('myapp', default_config=default_cfg)
On Linux, you would get
/etc/myapp.toml
as a system configuration,~/.myapp.toml
as a user configuration and$MYAPP_SITE/config.toml
as the local site.
- which(*path: str) str [source]#
Derive which member namespace takes precedent for the given variable.
- Example:
>>> conf = Configuration(one=Namespace({'x': 1, 'y': 2}), ... two=Namespace({'x': 3, 'z': 4})) >>> conf.extend(three=Namespace({'y': 5, 'u': {'i': 6, 'j': 7}}))
>>> conf.which('x') 'two'
>>> conf.which('y') 'three'
>>> conf.which('u', 'i') 'three'
- Note:
Care needs to be taken when used for mutable variables in the stack as the returned precedent does not reflect that the variable at that level may be a depth-first-merge of several sources.
>>> conf = Configuration(one=Namespace({'a': {'x': 1, 'y': 2}}), ... two=Namespace({'a': {'y': 3}}))
>>> conf.which('a') 'two'
>>> conf.a Namespace({'x': 1, 'y': 3})
- duplicates() Dict[str, Dict[str, List[Tuple[str, ...]]]] [source]#
Find all the repeated leaves.
- Example:
>>> one = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}}) >>> two = Namespace({'b': {'x': 4, 'z': 2}, 'c': {'j': True, 'k': 3.14}}) >>> cfg = Configuration(one=one, two=two)
>>> cfg.duplicates() {'x': {'one': [('a',), ('b',)], 'two': [('b',)]}, 'z': {'one': [('b',)], 'two': [('b',)]}}
- whereis(leaf: str, value: ~typing.Callable[[~cmdkit.config.T], bool] | ~cmdkit.config.T = <function Configuration.<lambda>>) Dict[str, List[Tuple[str, ...]]] [source]#
Find paths to leaf, optionally filtered on value, for each member namespace.
- Example:
>>> one = Namespace({'a': {'x': 1, 'y': 2}, 'b': {'x': 3, 'z': 4}}) >>> two = Namespace({'b': {'x': 4}, 'c': {'j': True, 'k': 3.14}}) >>> cfg = Configuration(one=one, two=two)
>>> cfg.whereis('x') {'one': [('a',), ('b',)], 'two': [('b',)]}
>>> cfg.whereis('x', 1) {'one': [('a',)], 'two': []}
>>> cfg.whereis('x', lambda v: v % 3 == 0) {'one': [('b',)], 'two': []}
- update(*args, **kwargs) None [source]#
Update current namespace directly.
- Note:
The
Configuration
class is itself aNamespace
-like object. Doing any in-place changes to its underlying self does not change its member namespaces. This may otherwise cause confusion about the provenance of those parameters. Instead, overrides have been implemented to capture these changes in a local namespace. If you askwhich()
namespace a parameter has come from, and it was an in-place change, it will be considered a member of the “_” namespace.- Example:
>>> conf = Configuration(a=Namespace(x=1)) >>> conf Configuration(a=Namespace({'x': 1}))
>>> conf.update(y=2) >>> conf Configuration(a=Namespace({'x': 1}), _=Namespace({'y': 2}))
>>> conf.x = 2 >>> conf Configuration(a=Namespace({'x': 1}), _=Namespace({'x': 2, 'y': 2}))
>>> conf.update(y=3) >>> conf Configuration(a=Namespace({'x': 1}), _=Namespace({'x': 2, 'y': 3}))
>>> dict(conf) {'x': 2, 'y': 3}