Configuration Loader

Public API for kstlib.config, covering the cascade helpers, loaders, and shortcuts used throughout the rest of the library. The module is intentionally small: most of the behaviour lives in kstlib.config.loader, but this namespace exposes the functions you are expected to import.

Tip

Pair this reference with Configuration for the feature guide.

Quick overview

  • load_from_file(path, *, strict_format=False, **overrides) reads a specific file (YAML/TOML/JSON/INI) and resolves any include: directives.

  • get_config(*, max_age=None, **overrides) executes the cascading search and memoizes the result so imports do not thrash the filesystem.

  • require_config() mirrors get_config() but raises immediately when no configuration has been loaded yet.

  • clear_config() flushes the memoized cascade, useful in tests when multiple fixtures run in the same process.

  • reload_config() is the ergonomic alias for “flush and reload from disk”, intended for interactive sessions (Jupyter, REPL) where the YAML files have been edited mid-session.


Core Functions

get_config

kstlib.config.get_config(filename: 'str' = 'kstlib.conf.yml', force_reload: 'bool' = False, max_age: 'float | None' = None) 'Box' -> Box[source]

Return the current kstlib configuration object (singleton).

Loads the configuration only once, unless force_reload=True is set.

This is a backward-compatible wrapper. For new code, prefer ConfigLoader class.

Parameters:
  • filename (str) – Name of the config file to search for.

  • force_reload (bool) – Force reloading the configuration from disk.

  • max_age (float | None) – Optional cache lifetime in seconds; refreshes automatically when the cached configuration is older than this value.

Returns:

Configuration object (dot notation enabled).

Return type:

Box

Raises:

ConfigFileNotFoundError – If no configuration file is found in any location.

Examples

>>> config = get_config()  
>>> config = get_config(force_reload=True)  

load_from_file

kstlib.config.load_from_file(path: 'str | pathlib.Path', strict_format: 'bool' = False, sops_decrypt: 'bool' = True) 'Box' -> Box[source]

Load configuration from a specific file path.

Convenience wrapper for load_config(path=…).

This is a backward-compatible wrapper. For new code, prefer ConfigLoader.from_file().

Parameters:
  • path (str | Path) – Path to configuration file (str or Path object).

  • strict_format (bool) – If True, included files must match parent file format.

  • sops_decrypt (bool) – If True, auto-decrypt .sops.* files via SOPS binary.

Returns:

Configuration object with dot notation support.

Raises:
  • ConfigFileNotFoundError – If the specified file doesn’t exist.

  • ConfigFormatError – On unsupported format or format mismatch.

  • ConfigCircularIncludeError – On circular includes.

Return type:

Box

Examples

>>> config = load_from_file("/opt/myapp/config.yml")  
>>> config = load_from_file("/etc/app.yml", strict_format=True)  

require_config

kstlib.config.require_config() 'Box' -> Box[source]

Return the configuration object, raising an exception if not loaded.

Use this when you need to ensure a config is available.

This is a backward-compatible wrapper. For new code, prefer ConfigLoader class.

Returns:

Loaded configuration.

Raises:

ConfigNotLoadedError – If configuration has not been loaded yet.

Return type:

Box

Examples

>>> config = require_config()  

clear_config

kstlib.config.clear_config() 'None' -> None[source]

Clear the singleton configuration cache.

Useful for testing or when you need to reload configuration.

Examples

>>> clear_config()  
>>> config = get_config()  # Will reload from disk  

reload_config

kstlib.config.reload_config(filename: 'str' = 'kstlib.conf.yml') 'Box' -> Box[source]

Force-reload the singleton configuration from disk.

Equivalent to clear_config() followed by get_config(), but explicit and discoverable in a single import. Designed for interactive sessions (Jupyter, REPL) where the underlying YAML files have been edited and the cached config needs to be refreshed without restarting the kernel.

Parameters:

filename (str) – Config filename to search for. Defaults to kstlib.conf.yml.

Returns:

Freshly loaded Box configuration object. The singleton cache is updated in-place, so subsequent get_config() calls return the same fresh object.

Raises:

ConfigFileNotFoundError – If no configuration file is found in any search location.

Return type:

Box

Note

When to use which:

  • reload_config(): explicit one-shot refresh (Jupyter/REPL).

  • get_config(force_reload=True): same effect, but the intent is hidden in a kwarg.

  • clear_config(): only flushes the cache; the next get_config() call triggers the actual reload. Useful in tests that want to isolate the cache boundary.

Examples

>>> from kstlib.config import reload_config
>>> cfg = reload_config()  
>>> cfg.mail.default  
'corporate'

ConfigLoader Class

ConfigLoader

class kstlib.config.ConfigLoader(strict_format=False, encoding='utf-8', sops_decrypt=True, *, auto=None, **auto_kwargs)[source]

Bases: object

Configuration loader with support for multiple formats and sources.

This class provides a clean, object-oriented interface for loading configuration from various sources with customizable behavior.

strict_format

If True, included files must match parent format.

encoding

File encoding for text-based formats.

sops_decrypt

If True, auto-decrypt .sops.* files via SOPS binary.

auto_discovery

Whether the constructor should immediately hydrate the config.

auto_source

Source used for auto-discovery (cascading/env/file).

auto_filename

Filename searched when auto-discovery cascades.

auto_env_var

Environment variable used when auto_source is "env".

auto_path

Explicit path used when auto_source is "file".

auto

AutoDiscoveryConfig carrying the effective auto-discovery options.

Examples

Instance-based usage with custom settings:

>>> loader = ConfigLoader(strict_format=True, encoding='utf-8')  
>>> config = loader.load_from_file("config.yml")  
>>> print(config.app.name)  

Factory methods (one-liner convenience):

>>> config = ConfigLoader.from_file("config.yml")  
>>> config = ConfigLoader.from_env("CONFIG_PATH")  
>>> config = ConfigLoader.from_cascading("myapp.yml")  

Multiple independent configs:

>>> dev_config = ConfigLoader().load_from_file("dev.yml")  
>>> prod_config = ConfigLoader(strict_format=True).load_from_file("prod.yml")  

Disable SOPS decryption:

>>> loader = ConfigLoader(sops_decrypt=False)  
>>> config = loader.load_from_file("secrets.sops.yml")  # Loads raw  
__init__(self, strict_format: 'bool' = False, encoding: 'str' = 'utf-8', sops_decrypt: 'bool' = True, *, auto: 'AutoDiscoveryConfig | None' = None, **auto_kwargs: 'Any') 'None' -> None[source]

Initialize a ConfigLoader with specific settings.

Parameters:
  • strict_format (bool) – If True, included files must match parent file format.

  • encoding (str) – File encoding for text-based configuration formats.

  • sops_decrypt (bool) – If True, auto-decrypt .sops.* files via SOPS binary.

  • auto (AutoDiscoveryConfig | None) – Pre-built auto-discovery options. When omitted, keyword arguments such as auto_source or auto_filename are honoured.

  • auto_kwargs (Any) – Legacy keyword arguments controlling auto-discovery: auto_discovery, auto_source, auto_filename, auto_env_var, and auto_path.

property cache: Box | None

Return the cached configuration instance, if any.

property cache_timestamp: float | None

Return the epoch timestamp when the cache was last refreshed.

property config: Box

Return the currently loaded configuration or raise if missing.

__getattr__(self, item: 'str') 'Any' -> Any[source]

Proxy attribute access to the cached configuration.

__getitem__(self, key: 'str') 'Any' -> Any[source]

Provide dict-style access to the cached configuration.

load_from_file(self, path: 'str | pathlib.Path', *, purge_cache: 'bool' = True) 'Box' -> Box[source]

Load configuration from a specific file path.

Parameters:
  • path (str | Path) – Path to configuration file (str or Path object).

  • purge_cache (bool) – If True, replace the cached config with the freshly loaded data.

Returns:

Configuration object with dot notation support.

Raises:
  • ConfigFileNotFoundError – If the specified file doesn’t exist.

  • ConfigFormatError – On unsupported format or format mismatch.

  • ConfigCircularIncludeError – On circular includes.

Return type:

Box

Examples

>>> loader = ConfigLoader()  
>>> config = loader.load_from_file("/opt/myapp/config.yml")  
>>> print(config.database.host)  
load_from_env(self, env_var: 'str' = 'CONFIG_PATH', *, purge_cache: 'bool' = True) 'Box' -> Box[source]

Load configuration from path specified in an environment variable.

Parameters:
  • env_var (str) – Name of environment variable containing config file path.

  • purge_cache (bool) – If True, replace the cached config with the freshly loaded data.

Returns:

Configuration object with dot notation support.

Raises:
  • ValueError – If environment variable is not set or empty.

  • ConfigFileNotFoundError – If the path in environment variable doesn’t exist.

Return type:

Box

Examples

>>> import os  
>>> os.environ["CONFIG_PATH"] = "/opt/config.yml"  
>>> loader = ConfigLoader()  
>>> config = loader.load_from_env()  
load(self, filename: 'str' = 'kstlib.conf.yml', *, purge_cache: 'bool' = True) 'Box' -> Box[source]

Load configuration using cascading search across multiple locations.

Search order (priority from lowest to highest):
  1. Package default config (lowest priority - base layer)

  2. System-wide config dirs from platformdirs.site_config_dir (respects XDG_CONFIG_DIRS on Linux, native locations on Mac/Windows). Missing files are skipped silently.

  3. User’s config directory (e.g., ~/.config/kstlib.conf.yml)

  4. User’s home directory (e.g., ~/kstlib.conf.yml)

  5. Current working directory (highest priority - overrides all)

Parameters:
  • filename (str) – Config filename to search for.

  • purge_cache (bool) – If True, replace the cached config with the freshly loaded data.

Returns:

Configuration object with dot notation support.

Raises:

ConfigFileNotFoundError – If no config file is found in any location.

Return type:

Box

Examples

>>> loader = ConfigLoader()  
>>> config = loader.load("myapp.yml")  
classmethod from_file(path: 'str | pathlib.Path', strict_format: 'bool' = False, encoding: 'str' = 'utf-8', sops_decrypt: 'bool' = True) 'Box' -> Box[source]

Create loader and load file in one call (factory method).

Parameters:
  • path (str | Path) – Path to configuration file.

  • strict_format (bool) – If True, included files must match parent format.

  • encoding (str) – File encoding.

  • sops_decrypt (bool) – If True, auto-decrypt .sops.* files via SOPS binary.

Returns:

Configuration object with dot notation support.

Return type:

Box

Examples

>>> config = ConfigLoader.from_file("config.yml")  
>>> config = ConfigLoader.from_file("config.yml", strict_format=True)  
classmethod from_env(env_var: 'str' = 'CONFIG_PATH', strict_format: 'bool' = False, encoding: 'str' = 'utf-8', sops_decrypt: 'bool' = True) 'Box' -> Box[source]

Create loader and load from environment variable in one call (factory method).

Parameters:
  • env_var (str) – Name of environment variable containing config file path.

  • strict_format (bool) – If True, included files must match parent format.

  • encoding (str) – File encoding.

  • sops_decrypt (bool) – If True, auto-decrypt .sops.* files via SOPS binary.

Returns:

Configuration object with dot notation support.

Return type:

Box

Examples

>>> config = ConfigLoader.from_env("CONFIG_PATH")  
>>> config = ConfigLoader.from_env("MYAPP_CONFIG", strict_format=True)  
classmethod from_cascading(filename: 'str' = 'kstlib.conf.yml', strict_format: 'bool' = False, encoding: 'str' = 'utf-8', sops_decrypt: 'bool' = True) 'Box' -> Box[source]

Create loader and perform cascading search in one call (factory method).

Parameters:
  • filename (str) – Config filename to search for.

  • strict_format (bool) – If True, included files must match parent format.

  • encoding (str) – File encoding.

  • sops_decrypt (bool) – If True, auto-decrypt .sops.* files via SOPS binary.

Returns:

Configuration object with dot notation support.

Return type:

Box

Examples

>>> config = ConfigLoader.from_cascading("myapp.yml")  
>>> config = ConfigLoader.from_cascading(strict_format=True)  

SOPS Integration

SopsDecryptor

class kstlib.config.SopsDecryptor(binary='sops', max_cache_entries=64)[source]

Bases: object

Lightweight SOPS decryptor with LRU cache.

This class provides SOPS file decryption with: - Configurable binary path - LRU cache with mtime-based invalidation - Clear error messages for troubleshooting

binary

Name or path of the SOPS binary.

max_cache

Maximum cache entries (clamped to hard limit).

Examples

>>> decryptor = SopsDecryptor()  
>>> content = decryptor.decrypt_file(Path("secrets.sops.yml"))  
__init__(self, binary: 'str' = 'sops', max_cache_entries: 'int' = 64) 'None' -> None[source]

Initialize the SOPS decryptor.

Parameters:
  • binary (str) – Name or path of the SOPS binary.

  • max_cache_entries (int) – Maximum number of cached decrypted files.

property binary: str

Return the configured SOPS binary name.

property max_cache: int

Return the maximum cache size.

decrypt_file(self, path: 'pathlib.Path') 'str' -> str[source]

Decrypt a SOPS-encrypted file and return content as string.

Parameters:

path (Path) – Path to the SOPS-encrypted file.

Returns:

Decrypted file content as a string.

Raises:
  • ConfigSopsNotAvailableError – If SOPS binary is not found.

  • ConfigSopsError – If decryption fails.

Return type:

str

purge_cache(self, path: 'pathlib.Path | None' = None) 'None' -> None[source]

Clear cache entries.

Parameters:

path (Path | None) – If provided, only clear this specific path. If None, clear all cached entries.

property cache_size: int

Return the current number of cached entries.

get_decryptor

kstlib.config.sops.get_decryptor(binary: 'str' = 'sops') 'SopsDecryptor' -> SopsDecryptor[source]

Get or create global SOPS decryptor singleton.

Parameters:

binary (str) – SOPS binary name (only used on first call).

Returns:

The global SopsDecryptor instance.

Return type:

SopsDecryptor

Examples

>>> decryptor = get_decryptor()  
>>> content = decryptor.decrypt_file(path)  

Exceptions

ConfigError

class kstlib.config.ConfigError[source]

Bases: KstlibError

Base exception for configuration-related errors.

All config module exceptions inherit from this class.

ConfigFileNotFoundError

class kstlib.config.ConfigFileNotFoundError[source]

Bases: ConfigError, FileNotFoundError

Configuration file not found at specified location.

Raised when attempting to load a config file that doesn’t exist.

ConfigNotLoadedError

class kstlib.config.ConfigNotLoadedError[source]

Bases: ConfigError, RuntimeError

Configuration not loaded yet.

Raised by require_config() when attempting to access config before it has been loaded via get_config() or load_config().