Package: ecological
EcologicalEcologicalcombinesPEP526and environment variables to make the configuration of12 factor appseasy.Getting StartedEcologicalautomatically gets and converts environment variables according to the configuration class definition.For example, imagine your application has a configurable (integer) Port and (boolean) Debug flag and a (string) Log
Level, that isINFOby default, you could simply declare your configuration as:classConfiguration(ecological.Config):port:intdebug:boollog_level:str="INFO"And then set the environment variablesPORT,DEBUGandLOG_LEVEL.Ecologicalwill automatically set the
class properties from the environment variables with the same (but upper cased) name.By default the values are set at the class definition type and assigned to the class itself (i.e. the class doesn’t need to be
instantiated). If needed this behavior can be changed (see the Autoloading section).TutorialThetutorialcan be used to get to know with the library’s basic features interactively.Typing SupportEcologicalalso supports some of the types defined inPEP484, for example:classConfiguration(ecological.Config):list_of_values:List[str]Will automatically parse the environment variable value as a list.NotePlease note that while this will ensureConfiguration.list_of_valuesis a list it will not check that it
contains only strings.Prefixed ConfigurationYou can also decide to prefix your application configuration, for example, to avoid collisions:classConfiguration(ecological.Config,prefix='myapp'):home:strIn this case thehomeproperty will be fetched from theMYAPP_HOMEenvironment property.Nested ConfigurationEcological.Configalso supports nested configurations, for example:classConfiguration(ecological.Config):integer:intclassNested(ecological.Config,prefix='nested'):boolean:boolThis way you can group related configuration properties hierarchically.AdvancedFine-grained ControlYou can control some behavior of how the configuration properties are set.It can be achieved by providing aecological.Variableinstance as the default
value for an attribute or by specifying global options on the class level:my_source={"KEY1":"VALUE1"}classConfiguration(ecological.Config,transform=lambdav,wt:v,wanted_type=int,...):my_var1:WantedType=ecological.Variable(transform=lambdav,wt:wt(v),source=my_source,...)my_var2:str# ...All possible options and their meaning can be found in the table below:OptionClass levelVariable levelDefaultDescriptionprefixyesnoNoneA prefix that is uppercased and prepended when a variable name
is derived from an attribute name.variable_nameyesyesDerived from attribute name and prefixed
withprefixif specified; uppercased.When specified on the variable level it states
the exact name of the source variable that will be used.When specified on the class level it is treated as a function
that returns a variable name from the attribute name with
the following signature:def func(attribute_name: str, prefix: Optional[str] = None)defaultnoyes(no default)Default value for the property if it isn’t set.transformyesyesA source value is casted to thewanted_typeIn case of non-scalar types (+ scalarbool)
the value is Python-parsed first.A function that converts a value from thesourceto the value
andwanted_typeyou expect with the following signature:def func(source_value: str, wanted_type: Union[Type, str])sourceyesyesos.environDictionary that the value will be loaded from.wanted_typeyesyesstrDesired Python type of the attribute’s value.On the variable level it is specified via a type annotation on
the attribute:my_var_1: my_wanted_type.However it can be also specified on the class level, then it acts
as a default when the annotation is not provided:class MyConfig(ecological.Config, wanted_type=int,...)The following rules apply when options are resolved:when options are specified on both levels (variable and class),
the variable ones take precedence over class ones,when some options are missing on the variable level, their default values
are taken from the class level,it is not necessary to assign anecological.Variableinstance to
change the behavior; it can still be changed on the class level (globally).AutoloadingIt is possible to defer/disable autoloading (setting) of variable values by specifying theautoloadoption on class definition.On class creation (default)When no option is provided values are loaded immediately on class creation and assigned to class attributes:classConfiguration(ecological.Config):port:int# Values already read and set at this point.# assert Configuration.port == <value-of-PORT-env-var>NeverWhen this option is chosen, no autoloading happens. In order to set variable values, theConfig.loadmethod needs to be called explicitly:classConfiguration(ecological.Config,autoload=ecological.Autoload.NEVER):port:int# Values not set at this point.# Accessing Configuration.port would throw AttributeError.Configuration.load()# Values read and set at this point.# assert Configuration.port == <value-of-PORT-env-var>On object instance initializationIf it is preferred to load and store attribute values on the object instance instead of the class itself, theAutoload.OBJECTstrategy can be used:classConfiguration(ecological.Config,autoload=ecological.Autoload.OBJECT):port:int# Values not set at this point.config=Configuration()# Values read and set at this point on ``config``.# assert config.port == <value-of-PORT-env-var># Accessing ``Configuration.port`` would throw AttributeError.Caveats and Known LimitationsEcologicaldoesn’t support (public) methods inConfigclasses