Package: confz
ConfZ – Pydantic Config ManagementConfZis a configuration management library for Python based onpydantic.
It easily allows you toload your configuration from config files, environment variables, command line arguments and moretransform the loaded data into a desired format and validate itaccess the results as Python dataclass-like objects with full IDE supportIt furthermore supports you in common use cases like:Multiple environmentsSingleton with lazy loadingConfig changes for unit testsCustom config sourcesUPDATE: ConfZ 2 is here, with support for pydantic 2 and improved naming conventions.
Check out themigration guide.:package: InstallationConfZis onPyPIand can be installed with pip:pipinstallconfz:rocket: Quick StartThe first step of usingConfZis to declare your config classes and sources, for example inconfig.py:fromconfzimportBaseConfig,FileSourcefrompydanticimportSecretStr,AnyUrlclassDBConfig(BaseConfig):user:strpassword:SecretStrclassAPIConfig(BaseConfig):host:AnyUrlport:intdb:DBConfigCONFIG_SOURCES=FileSource(file='/path/to/config.yml')Thanks topydantic, you can use a wide variety offield typesandvalidators.From now on, in any other file, you can access your config directly:fromconfigimportAPIConfigprint(f"Serving API at{APIConfig().host}, port{APIConfig().port}.")As can be seen, the config does neither have to be loaded explicitly, nor instantiated globally.ConfZautomatically
loads your config as defined inCONFIG_SOURCESthe first time you access it. Thanks to its singleton mechanism, this
happens the first time only, afterwards you get back a cached,immutableinstance, behaving like any otherpydanticinstance.assertAPIConfig()isAPIConfig()# true because of singleton mechanismAPIConfig().port=1234# raises an error because of immutabilityAPIConfig().model_dump()# call pydantic's method to get a dict representationNote:While the implicit and hidden loading of your config might be surprising and feel a bit like Python magic at
first, it allows you to reduce a lot of boilerplate. Instead of having to load your config explicitly and then passing
it down to all code layers that need it, you can directly access it from anywhere by just importing your config class
and accessing for exampleAPIConfig().db.userdirectly.More Config SourcesConfZis highly flexible in defining the source of your config. Do you have multiple environments? No Problem:fromconfzimportBaseConfig,FileSourceclassMyConfig(BaseConfig):...CONFIG_SOURCES=FileSource(folder='/path/to/config/folder',file_from_env='ENVIRONMENT')Your config file can now be defined in the environment variableENVIRONMENTand is relative tofolder.You can also provide a list as config source and read for example from environment variables including a .env file and
from command line arguments:fromconfzimportBaseConfig,EnvSource,CLArgSourceclassMyConfig(BaseConfig):...CONFIG_SOURCES=[EnvSource(allow_all=True,file=".env.local"),CLArgSource(prefix='conf_')]ConfZnow tries to populate your config either from environment variables having the same name as your attributes or
by reading command line arguments that start withconf_. Recursive models are supported too, for example if you want
to control the user-name in the API above, you can either set the environment variableDB.USERor pass the command
line argument--conf_db.user.Explicit LoadingIn some scenarios, the config should not be a global singleton, but loaded explicitly and passed around locally.
Instead of definingCONFIG_SOURCESas class variable, the sources can also be defined in the constructor directly:fromconfzimportBaseConfig,FileSource,EnvSourceclassMyConfig(BaseConfig):number:inttext:strconfig1=MyConfig(config_sources=FileSource(file='/path/to/config.yml'))config2=MyConfig(config_sources=EnvSource(prefix='CONF_',allow=['text']),number=1)config3=MyConfig(number=1,text='hello world')As can be seen, additional keyword-arguments can be provided as well.Note:If neither class variableCONFIG_SOURCESnor constructor argumentconfig_sourcesis provided,BaseConfigbehaves like a regularpydanticclass.Change Config ValuesIn some scenarios, you might want to change your config values, for example within a unit test. However, if you set theCONFIG_SOURCESclass variable, this is not directly possible. To overcome this, every config class provides a context
manager to temporarily change your config:fromconfzimportBaseConfig,FileSource,DataSourceclassMyConfig(BaseConfig):number:intCONFIG_SOURCES=FileSource(file="/path/to/config.yml")print(MyConfig().number)# will print the value from the config-filenew_source=DataSource(data={'number':42})withMyConfig.change_config_sources(new_source):print(MyConfig().number)# will print '42'print(MyConfig().number)# will print the value from the config-file againEarly ValidationBy default, your config gets loaded the first time you instantiate the class, e.g. withMyConfig().attribute. This
prevents side effects like loading a file while you import your config classes. If the config class cannot populate all
mandatory fields in the correct format,pydanticwill raise an error at this point. To make sure this does not happen
in an inconvenient moment, you can also instructConfZto load all configs beforehand:fromconfzimportvalidate_all_configsif__name__=='__main__':validate_all_configs()# your application codeThe functionvalidate_all_configswill instantiate all config classes defined in your code at any (reachable)
location that haveCONFIG_SOURCESset.:book: DocumentationNow you've seen the two ways howConfZcan be used: With class variable config sources, unlocking a singleton with
lazy loading, or with keyword argument config sources, allowing to directly load your config values. In both cases,
defining your config sources from files, command line arguments and environment variables is highly flexible
(and also extendable, by the way), whilepydanticstill makes sure that everything matches your expectations in the
end. You've also seen how to temporarily change your config for example in unit tests and how to validate
your singleton config classes early in the code already.The full documentation ofConfZ's features can be found atreadthedocs.:information_source: AboutConfZwas programmed and will be maintained byZühlke.
The first version was realized bySilvan.
Special thanks to Iwan with hisConfMe, which inspired this project.Want to contribute toConfZ? Check out the contributioninstruction & guidelines.