lightbulb.config¶
This module contains utilities for parsing your application configuration from various file types. Along with basic deserializing to Python objects, it implements environment variable substitution expressions to allow “dynamic” values within the configuration.
These utilities were originally implemented within lightbulb, but since have been extracted and are now
provided by the confspec
library (PyPI).
Environment Variable Substitution¶
The basic syntax for defining a variable substitution is as follows:
${VARIABLE_NAME}
This would be replaced by the loader with the value of the environment variable VARIABLE_NAME
while parsing
the configuration. If the variable is not set, then this will evaluate to an empty string.
Expanding on this, you can also specify default values for when a variable is not set:
${VARIABLE_NAME:default-value}
This will expand to the value of VARIABLE_NAME
, if it is set, otherwise it will expand to whatever you specified
as the default value.
Sometimes you may want to include text that uses the ${NAME}
format directly, without any substitution. This
can be achieved by escaping the substitution expression with an additional $
symbol:
$${VARIABLE_NAME}
This will not be expanded using the environment variables, and instead one of the leading $
symbols will be
stripped, causing it to evaluate as ${VARIABLE_NAME}
.
confspec
provides additional substitution features, you should consult its documentation for further details.
Usage¶
confspec
supports parsing configuration into a variety of formats - standard dictionaries, msgspec Structs, or
pydantic models. The below example will use msgspec Structs, but you can choose whichever you are most comfortable
with. Just ensure the correct dependencies are installed first.
You should create a configuration file for your application - supported
formats are yaml
, toml
and json
- and a msgspec.Struct
-derived class that your configuration
will be deserialized to.
config.yaml
foo: bar
baz: ${INT_VAR}
bork:
qux: quux
config.py
class BorkConfig(msgspec.Struct):
qux: str
class Config(msgspec.Struct):
foo: str
baz: int
bork: BorkConfig
Then to parse the config file into your models, simply call the load()
method:
import lightbulb
# your configuration models
from app import config
parsed_cfg = lightbulb.config.load("path/to/config.yaml", cls=config.Config)
# prints "bar"
print(parsed_cfg.foo)
- load(path: str | Path, /, *, cls: type[pydantic.BaseModel | Struct] | None = None, strict: bool = False, dec_hook: Callable[[type[t.Any], t.Any], t.Any] | None = None) dict[str, t.Any] | pydantic.BaseModel | Struct [source]¶
Loads arbitrary configuration from the given path, performing environment variable substitutions, and parses it into the given class (or to a dictionary if no class was provided).
Currently supported formats are: yaml, toml and JSON. Additional formats can be supported by creating your own custom implementation of
Parser
and registering it withparser_registry
.- Parameters:
path – The path to the configuration file.
cls – The pydantic BaseModel, or msgspec Struct to parse the configuration into. If
None
, the configuration will be parsed into a dictionary. Defaults toNone
.strict – Whether the parsing behaviour of pydantic/msgspec should be in strict mode. Defaults to
False
. IfTrue
, then parsers will not perform type coercion (e.g. digit string to int).dec_hook – Optional decode hook for msgspec to use when parsing to allow supporting additional types.
- Returns:
The parsed configuration.
- Raises:
NotImplementedError – If a file with an unrecognized format is specified.
ValueError – If the file cannot be parsed to a dictionary (e.g. the top level object is an array).
ImportError – If a required dependency is not installed.
- loads(raw: str | bytes, fmt: KnownFormats | str, /, *, cls: type[pydantic.BaseModel | Struct] | None = None, strict: bool = False, dec_hook: Callable[[type[t.Any], t.Any], t.Any] | None = None) dict[str, t.Any] | pydantic.BaseModel | Struct [source]¶
Like
load()
, but loads the configuration from the given string or bytes object instead. You must pass a format when using this method so that the library knows which parser to use. All other arguments have the same meaning as inload()
.