Package: load-environ-typed
load-environ-typedThis library lets you declare a NamedTuple or dataclass with
field types, and gives you a function that willloadthe
values from the environment, validating your types for you.Getting startedfromload_environ_typedimportloadfromdataclassesimportdataclass@dataclassclassMyEnviron:db_host:strdb_port:intenviron=load(MyEnviron)FAQWhat types can I use? What about custom types?By default, any type that takes a single string in it's constructor can be used
as type. Think ofint,str,float,pathlib.Path, etc.We've added default loaders for the types below:bool- "true" or "false", case insenstivedatetime.date- Usingdatetime.date.fromisoformatdatetime.time- Usingdatetime.time.fromisoformatdatetime.datetime- Usingdatetime.datetime.fromisoformatHow are fields matched to enviroment variables?The loader assumes the names are the same, except that the class fields
are lowercase, and the environment fields are uppercase. If you have
different or more complicated rules, you can pass a name conversion
function viafield_name_to_var_name.@dataclassclassMyEnviron:iso_date:datetime.dateenviron=sut.load(MyEnviron,environ={'ISO_DATE':'2021-01-01',})Can values be optional?Certainly:@dataclassclassMyEnviron:DB_HOST:Optional[str]However:empty string and "none" (case insensitive) count asNonedefaults take precedence over optionalityWhat if my type cannot take a string in its constructor?You can pass so-called loader functions. These take in a string, and are
expected to return a value of the given type, or raise aValueErrorwhen
the given string is not valid for the given type. This is also the
mechanism that we use to support standard Python types suchdatetime.date,
which is shown below.@dataclassclassMyEnviron:ISO_DATE:datetime.dateenviron=sut.load(MyEnviron,environ={'ISO_DATE':'2021-01-01',},loaders={'ISO_DATE':datetime.date.fromisoformat,# or, if you want ALL `date`s to use this loader:datetime.date:datetime.date.fromisoformat,})NOTE: date has a default loader, so you don't need to do this fordate.Can I use Unions?You can, but you need to use a default loader, as you need some
way to distinguish between the types, and there is no general
way to do so, at least not without enforcing our way of working on you.How do I work with default values?If you want default values, it's probably best to have use a dataclass
withkw_only=True, as otherwise you have to order your variables based
on whether there's a default or not.@dataclass(kw_only=True)classMyEnviron:DB_HOST:strDB_PORT:int=3306For most types, you can simply set the default value as you're used to
with dataclasses. However, you may not want to instantiate an (expensive)
property as default. In those cases, you can pass defaults along using
thedefaultsargument.@dataclassclassMyEnviron:VAR:SomeExpensiveClassenviron=load(MyEnviron,defaults={'VAR':'#!serialized.data!#',})NOTE:kw_onlyrequires Python3.10 or higher. Below 3.10, you can use
thedefaultsargument or order your variables. Similarly, if you plan
on solely using thedefaultsargument, you don't needkw_only.Load returns an instance. What if I want a global?Sinceenvironis available at startup, and doesn't change, it's perfectly
valid to just instantiate a global variable. You should probably use a
frozen dataclass for this.@dataclass(frozen=True)classMyEnviron:DB_HOST:strDB_PORT:intENVIRON=load(MyEnviron)What if there's an issue with the default loaders?First, the loaders you pass will be taken before using the default loaders.Second, if you have more structural issues with the default loaders, simply
passuse_default_loaders=False.ContributingCheck out this repo, and then use the following steps to test it:python3-mvenvvenv
venv/bin/pipinstall-rrequirements-dev.txt
maketestDeployingFirst, update pyproject.toml with the new version number, and commit that.Then:rm-fdist/*# Clean old build filesvenv/bin/python-mbuild
venv/bin/python-mtwineuploaddist/*