Skip to content

Instantly share code, notes, and snippets.

@Tinche
Created September 16, 2021 23:23
Show Gist options
  • Save Tinche/30a0c7a98f01c96c6bc63c7a34768d7b to your computer and use it in GitHub Desktop.
Save Tinche/30a0c7a98f01c96c6bc63c7a34768d7b to your computer and use it in GitHub Desktop.
Environment variable loading using cattrs
from os import environ
from typing import TypeVar
from attr import NOTHING, define, fields
from cattr import GenConverter
from cattr._compat import is_sequence
@define
class MyEnvVars:
"""Model your environment variables as members of this class."""
PWD: str
PATH: list[str]
USER: str
INT_LIST: list[int]
NONEXISTENT: str = "MyDefault"
c = GenConverter()
def gen_structure_envvar_seq(type):
def structure_envvar_seq(encoded_seq: str, _) -> list:
return [
c.structure(e, type.__args__[0]) for e in encoded_seq.split(":")
]
return structure_envvar_seq
c.register_structure_hook_factory(is_sequence, gen_structure_envvar_seq)
T = TypeVar("T")
def load_env_vars(cls: type[T], vars: dict[str, str] = environ) -> T:
obj = {
f.name: vars[f.name]
for f in fields(cls)
if f.default is NOTHING or f.name in vars
}
return c.structure(obj, cls)
print(load_env_vars(MyEnvVars))
@Tinche
Copy link
Author

Tinche commented Sep 18, 2021

I yesterday also found c._structure_list() – would this be a better alternative to the list comp. in your example?

It's kind of an internal API, but sure, it's not going anywhere.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment