2020-12-17

Stop doing mad config

People do all sorts of wild and wonderful things in Python to get configuration from files/the environment, then even stranger things attaching said config to applications.

Here's how everyone should do it, in a way that's (in vague order of importance):

from dataclasses import dataclass
from functools import lru_cache
import os
from pathlib import Path


@dataclass
class Config:
    DB_URL: str
    NUMBER_OF_WORKERS: int
    COMPLICATED_THING: dict


@lru_cache(None)
def get_config() -> Config:
    complicated_path = Path(__file__).parent / "some-file.json"
    return Config(
        DB_URL=os.environ["DB_URL"],
        NUMBER_OF_WORKERS=int(os.environ.get("N_WORKERS", 4)),
        COMPLICATED_THING=json.loads(complicated_path.read_bytes()),
    )

When you want to use it, do:

from my_service import config

def create_application():
    engine = Engine(url=config.get_config().DB_URL)
    ...

When you want to test it, do:

@pytest.fixture
def dummy_config(monkeypatch):
    monkeypatch.setattr(config, "get_config", lambda: Config(...))

def test_thingie(dummy_config):
    ...

Now no more funny stuff OK.