Package: coveo-settings
coveo-settingsWhenever you want the user to be able to configure something through an environment variable, this module has your back:fromcoveo_settingsimportStringSetting,BoolSettingDATABASE_URL=StringSetting('project.database.url')DATABASE_USE_SSL=BoolSetting('project.database.ssl')The user can then configure the environment variablesproject.database.urlandproject.database.sslto configure the application.When accessed, the values are automatically converted to the desired type:StringSettingwill always be a stringBoolSettingis either True or False, but accepts "yes|no|true|false|1|0" as input (case-insensitive, of course)IntSettingandFloatSettingare self-explanatoryDictSettingallows you to use JSON mapsPathSettinggives a Path instance, and also implements PathLike and the/operatorIf the input cannot be converted to the value type, anTypeConversionConfigurationErrorexception is raised.A default (fallback) value may be specified. The fallback may be acallable.A validation callback may be specified for custom logic and error messages.Not limited to environment variables; supports redirection to custom implementations.A setting can be set as sensitive for logging purposes. When logging, use repr(setting) to get the correct representation.Accessing the valueThere are various ways to obtain the value:fromcoveo_settingsimportBoolSettingDATABASE_USE_SSL=BoolSetting('project.database.ssl')# this method will raise an exception if the setting has no value and no fallbackuse_ssl=bool(DATABASE_USE_SSL)use_ssl=DATABASE_USE_SSL.get_or_raise()assertuse_sslin[True,False]# this method will not raise an exceptionuse_ssl=DATABASE_USE_SSL.valueassertuse_sslin[True,False,None]# use "is_set" to check if there is a value set for this setting; skips validation checkifDATABASE_USE_SSL.is_set:use_ssl=bool(DATABASE_USE_SSL)# use "is_valid" to verify if the value passes the validation callback. implies is_set.ifnotDATABASE_USE_SSL.is_valid:...Loose environment key matchingMatching the key of the environment variableproject.database.sslis done very loosely:case-insensitivedots and underscores are ignored completely (foo_barandf__ooba.rare equal)useful for some runners that don't support dots in environment variable keysUse ready validationYou can quickly validate that a string is in a specific list like this:fromcoveo_settings.settingsimportStringSettingfromcoveo_settings.validationimportInSequenceENV=StringSetting("environment",fallback="dev",validation=InSequence("prod","staging","dev"))RedirectionYou can register custom redirection schemes in order to support any data source.Much likehttps://is a clear scheme, you may register callback functions to trigger when the value of a setting
starts with the scheme(s) you define. For instance, let's support a custom API and a file storage:fromcoveo_settingsimportsettings_adapter,StringSetting,ConfigValue@settings_adapter("internal-api::")definternal_api_adapter(key:str)->ConfigValue:# the scheme was automatically removed for convenience; only the resource remainsassert"internal-api::"notinkeyreturn"internal api"# implement logic to obtain value from internal api@settings_adapter("file::",strip_scheme=False)deffile_adapter(key:str)->ConfigValue:# you can keep the scheme by specifying `strip_scheme=False`assertkey.startswith("file::")return"file adapter"# implement logic to parse the key and retrieve the setting valueassertStringSetting('...',fallback="internal-api::settings/user-name").value=="internal api"assertStringSetting('...',fallback="file::settings.yaml/user-name").value=="file adapter"# even though we used `fallback` above, the redirection is driven by the user:importosREDIRECT_ME=StringSetting('test')os.environ['test']="file::user.json::name"assertREDIRECT_ME.value=="file adapter"os.environ['test']="internal-api::url"assertREDIRECT_ME.value=="internal api"Keep in mind that there's no restriction on the prefix scheme; it's your responsibility to pick something unique
that can be set as the value of an environment variable.Redirection is recursiveThe value of a redirection may be another redirection and may juggle between adapters.
A limit of 50 redirections is supported:importosfromcoveo_settingsimportStringSettingos.environ["expected"]="final value"os.environ["redirected"]="env->expected"os.environ["my-setting"]="env->redirected"assertStringSetting("my-setting").value=="final value"Builtin environment redirectionThe builtin redirection schemeenv->can be used to redirect to a different environment variable.
The example below demonstrates the deprecation/migration ofmy-settingintonew-setting:importosfromcoveo_settingsimportStringSettingos.environ["new-setting"]="correct-value"os.environ["my-setting"]="env->new-setting"assertStringSetting("my-setting").value=="correct-value"CachedYou can set a setting to cache the first valid value withcached=True.
This is particularly useful in redirection scenarios to avoid repeating requests too often.Setting the valueYou can override the value usingsetting.value = "some value"and clear the override withsetting.value = None.
Clearing the override resumes the normal behavior of the environment variables and the fallback value, if set.This is typically used as a way to propagate CLI switches globally.
For mocking scenarios, refer to theMockingsection below.MockingWhen you need a setting value for a test, use themock_config_valuecontext manager:fromcoveo_settingsimportStringSettingfromcoveo_settings.mockimportmock_config_valueSETTING=StringSetting(...)assertnotSETTING.is_setwithmock_config_value(SETTING,'new-value'):assertSETTING.is_setYou can also clear the value:fromcoveo_settingsimportStringSettingfromcoveo_settings.mockimportmock_config_valueSETTING=StringSetting(...,fallback='test')assertSETTING.is_setwithmock_config_value(SETTING,None):assertnotSETTING.is_set