Skip to content

Commit

Permalink
feat(env): cls.from_env function to load config from env (os.environ …
Browse files Browse the repository at this point in the history
…or dotenv)
  • Loading branch information
robinvandernoord committed Nov 7, 2023
1 parent defa0a1 commit 1b16350
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 2 deletions.
42 changes: 40 additions & 2 deletions src/configuraptor/abs.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
"""
Contains the Abstract config class shared by TypedConfig and BinaryConfig.
"""

import os
import types
import typing
from pathlib import Path

import dotenv

# T is a reusable typevar
T = typing.TypeVar("T")
# t_typelike is anything that can be type hinted
Expand All @@ -22,7 +24,7 @@

class AbstractTypedConfig:
"""
Logic shared by TypedConfig and BinaryConfig.
These functions only exist on the class, not on instances.
"""

@classmethod
Expand All @@ -45,3 +47,39 @@ def load(
return load_into(
cls, data, key=key, init=init, strict=strict, lower_keys=lower_keys, convert_types=convert_types
)

@classmethod
def from_env(
cls: typing.Type[C],
load_dotenv: str | bool = False,
init: dict[str, typing.Any] = None,
strict: bool = True,
convert_types: bool = False,
) -> C:
"""
Create an instance of the typed config class by loading environment variables and initializing \
object attributes based on those values.
Args:
cls (typing.Type[C]): The class to create an instance of.
init (dict[str, typing.Any], optional): Additional initialization data to be used
in the object creation. Defaults to None.
strict (bool, optional): If True, raise an error if any required environment variable
is missing. Defaults to True.
convert_types (bool, optional): If True, attempt to convert environment variable values
to the appropriate Python types. Defaults to False.
load_dotenv (str | bool, optional): Path to a dotenv file or True to load the default
dotenv file. If False, no dotenv file will be loaded. Defaults to False.
Returns:
C: An instance of the class `C` with attributes initialized based on the environment variables.
"""
from .core import load_into

if load_dotenv:
dotenv_path = load_dotenv if isinstance(load_dotenv, str) else None
dotenv.load_dotenv(dotenv_path)

data = {**os.environ}

return load_into(cls, data, lower_keys=True, init=init, strict=strict, convert_types=convert_types)
24 changes: 24 additions & 0 deletions tests/test_dotenv.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os
import tempfile
import typing

import pytest
Expand Down Expand Up @@ -62,3 +64,25 @@ def test_dotenv_basic():
assert "from `<class 'str'>`" in str(e)
assert "to `<class 'bool'>`" in str(e)
raise e


class EnvConfig(configuraptor.TypedConfig):
from_my_env: str


def test_from_env():
os.environ["FROM_MY_ENV"] = "Example"

conf = EnvConfig.from_env()

assert conf.from_my_env == "Example"

del os.environ["FROM_MY_ENV"]

with tempfile.NamedTemporaryFile() as f:
f.write(b"FROM_MY_ENV=SECOND")
f.seek(0)

conf = EnvConfig.from_env(f.name)

assert conf.from_my_env == "SECOND"

0 comments on commit 1b16350

Please sign in to comment.