Card 04/ 09
Singleton Example: An Application Configuration
Read the settings file once, and give every part of the program the same settings.
An application's configuration is the most common Singleton: the settings file should be read once, and every module should see the same values. This example builds a Config singleton in a small package, uses it from a billing module and an email module, and shows what those modules now depend on.
import json
from pathlib import Path
from typing import Optional
class Config:
_instance: Optional["Config"] = None
def __init__(self, path: Path) -> None:
print(f"reading {path.name}")
self.values = json.loads(path.read_text())
@classmethod
def instance(cls) -> "Config":
if cls._instance is None:
cls._instance = cls(Path(__file__).with_name("settings.json"))
return cls._instancefrom sgconf.config import Config
def invoice_total(net: float) -> str:
config = Config.instance()
return f"{net * (1 + config.values['tax_rate']):.2f} {config.values['currency']}"from sgconf import billing, emails
from sgconf.config import Config
print(billing.invoice_total(100))
print(emails.footer())
print(billing.invoice_total(40))
print("one Config for both modules:", Config.instance() is Config.instance())reading settings.json
120.00 GBP
Questions? Write to [email protected]
48.00 GBP
one Config for both modules: Truereading settings.json appears once, although the configuration was used three times from two modules, and the last line confirms both modules received the same object. Every part of the program agrees on the currency and the tax rate because there is only one place they are held.
Look at invoice_total(net) from outside. Its signature says it needs a number. It also needs a settings file at a particular path, read through a class it never mentions in its parameters, and any test of it reads that file too. That hidden dependency is the price of the global access point, and in tests it lets one test change what the next one reads.
← → move through sections and cards