pi_weather.config.config_handler module

Logic for the config handler

Thread-safe singleton class that loads the TOML config file once and makes it accessible from anywhere in the project.

Usage:

# At the very start of your entry point (optional) (NOTE: Only called once) Config.set_path(“config/prod.toml”)

# Anywhere else in the codebase host = Config().get(“db”, “host”, default=”localhost”) port = Config().get(“db”, “port”, default=5432, cast=True) # “5432” -> 5432 fresh = Config().get(“db”, “host”, refresh=True) # re-read from disk first

Warning: The first Config() call must be called after Config.set_path(…).

class pi_weather.config.config_handler.Config(*args, **kwargs)[source]

Bases: object

Singleton config handler backed by a TOML file.

Return type:

Config

_instance: ClassVar[Config | None] = None
_settings: ClassVar[Dict[str, Any] | None] = None
_config_path: ClassVar[Path] = PosixPath('config/config.toml')
_path_locked: ClassVar[bool] = False
_lock: ClassVar[lock] = <unlocked _thread.lock object>
static __new__(cls, *args, **kwargs)[source]

(Real c-tor) Instantiate Config, importing settings on cold start.

Parameters:
  • args – Positional arguments

  • kwargs – Keyword arguments

Raises:

SettingsNotImportedException – if the TOML file can’t be read/parsed

Return type:

Config

Returns:

(Config)

classmethod set_path(filename)[source]

Set the config file path. Must be called before the first instantiation/access - the path is locked in after that (either because the singleton was created, or because set_path() already ran once successfully).

Parameters:

filename (str | Path) – Path to the config.toml file

Raises:

ConfigPathAlreadySetException – if the path is already locked in

Return type:

None

classmethod _import_settings()[source]

Import settings from cls._config_path. Keys are lowercased recursively (values are left untouched) so lookups are case-insensitive regardless of how the TOML file is written.

Returns:

(success, settings dict)

Return type:

Tuple[bool, Dict[str, Any]]

classmethod _lowercase_keys(obj)[source]

Recursively lowercase all dict keys (values untouched), including dicts nested inside lists (e.g. TOML arrays of tables).

Parameters:

obj (Any) – Object to normalize

Returns:

Object with all nested dict keys lowercased

Return type:

Any

classmethod reload()[source]

Force a reload of settings from disk (e.g. you edited the file on disk and don’t want to restart the process).

Returns:

True if reload succeeded (settings replaced), False otherwise

Return type:

bool

classmethod force_reload()[source]

Explicit alias for reload() - re-reads the config file from disk and updates _settings in place, independent of any get() call. Call this whenever you know the on-disk file changed, e.g.:

Config.force_reload()

Returns:

True if reload succeeded, False otherwise

Return type:

bool

get_settings(refresh=False)[source]

Return the full settings dictionary.

Parameters:

refresh (bool) – Reload from disk before returning

Returns:

Settings dictionary, empty dict if none loaded

Return type:

Dict[str, Any]

get(*args, default=None, refresh=False, cast=False)[source]

Get a (possibly nested) key from settings.

Parameters:
  • args (str) – Sequence of nested keys, e.g. get(“db”, “host”)

  • default (Any | None) – Value returned if the key path doesn’t exist

  • refresh (bool) – Reload settings from disk before reading

  • cast (bool) – Attempt to type-cast string values to bool/int/float/None before returning. Only affects strings - values TOML already parsed as numbers/bools are untouched.

Returns:

Retrieved value (or default)

Return type:

Any

static _maybe_cast(value)[source]

Best-effort cast of a string value to bool/int/float/None. Non-string values are returned unchanged.

Parameters:

value (Any) – Value to cast

Returns:

Casted value, or original value if no cast applies

Return type:

Any

__getattr__(key)[source]

Dict-style attribute access, e.g. Config().db instead of .get(“db”). Note: this only fires when normal attribute lookup fails, so a TOML key named e.g. “get” or “reload” would be shadowed by the real method - use get() explicitly for keys that might collide.

Parameters:

key (str) – Key name

Returns:

Settings value matching key, or None if missing

Return type:

Any | None