Skip to main content

Global settings runtime extension for FastAPIEx

Project description

fastapiex-settings

Process-global settings declaration and resolution runtime based on Pydantic v2.

Installation

uv add fastapiex-settings

Public API (Only These Are Exported)

from fastapiex.settings import (
    BaseSettings,
    Settings,
    SettingsMap,
    GetSettings,
    GetSettingsMap,
    SettingsRef,
    init_settings,
    reload_settings,
    exceptions,
)

No other symbol is part of the package root public API.

Quick Start

from pydantic import Field
from fastapiex.settings import (
    BaseSettings,
    Settings,
    SettingsMap,
    GetSettings,
    GetSettingsMap,
    init_settings,
    reload_settings,
)


@Settings("app")
class AppSettings(BaseSettings):
    title: str = "demo"
    debug: bool = False


@SettingsMap("services")
class ServiceSettings(BaseSettings):
    host: str
    port: int = Field(ge=1, le=65535)


init_settings(settings_path="settings.yaml")

title = GetSettings("app", field="title")
services = GetSettingsMap(ServiceSettings)  # dict[str, ServiceSettings]
api_host = GetSettings("services.api", field="host")
reload_settings(reason="manual-refresh")

Public API Reference

BaseSettings

class BaseSettings(pydantic.BaseModel): ...
  • Base class for declaration models.
  • Inheriting this class does not register anything by itself.

Settings

@overload
def Settings(model: type[BaseSettings], /) -> type[BaseSettings]: ...

@overload
def Settings(path: str | None = None, /) -> Callable[[type[BaseSettings]], type[BaseSettings]]: ...

Role:

  • Declare an object section.

Section name resolution order:

  1. explicit decorator path
  2. model __section__ if non-empty
  3. snake_case class name

Accepted forms:

  • @Settings
  • @Settings("father.son")
  • Settings(MyModel) (functional form)

SettingsMap

@overload
def SettingsMap(model: type[BaseSettings], /) -> type[BaseSettings]: ...

@overload
def SettingsMap(path: str | None = None, /) -> Callable[[type[BaseSettings]], type[BaseSettings]]: ...

Role:

  • Declare a map section (shape like dict[str, Model]).

Accepted forms and section naming:

  • Same behavior as Settings.

GetSettings

def GetSettings(
    target: str | type[object] | None = None,
    *,
    field: str | None = None,
    default: object = _NO_DEFAULT,
) -> Any

Role:

  • Resolve settings values from the current typed snapshot.

Parameters:

  • target:
    • str: dotted path, for example "app" / "father.son" / "services.api".
    • type: type-category injection; must match exactly one declared section.
    • None: only useful with default (otherwise unresolved).
  • field:
    • optional dotted sub-path applied after target is resolved.
    • blank string is invalid.
  • default:
    • returned as the whole query fallback value.
    • not projected by field.

Important semantics:

  • Read chain: registered lookup -> rediscover+lookup -> default -> error.
  • Incomplete path returns the current node as-is (model/dict/scalar/list element).
  • Mapping type target (dict, Mapping) resolves only when exactly one @SettingsMap section exists.
  • Returned objects are live runtime references (mutable); changes affect subsequent reads in the same process.

GetSettingsMap

def GetSettingsMap(
    target: str | type[object] | None = None,
    *,
    default: object = _NO_DEFAULT,
) -> Mapping[str, Any]

Role:

  • Resolve settings as mapping-only API.

Parameters:

  • target:
    • same semantics as GetSettings.
  • default:
    • whole-query fallback value.
    • must be mapping when fallback path is used.

Behavior:

  • Final value must be mapping; otherwise SettingsResolveError.
  • Returned mapping is a live runtime reference (mutable), not a defensive copy.

SettingsRef

@dataclass(frozen=True)
class SettingsRef:
    target: str | type[object] | None
    field: str | None = None
    default: object = _NO_DEFAULT

    def get(self) -> Any: ...
    @property
    def value(self) -> Any: ...
    def __call__(self) -> Any: ...

Role:

  • Lazy settings query descriptor.

Behavior:

  • get(), .value, and () perform the same runtime resolve.
  • Resolution semantics are identical to GetSettings.

init_settings

def init_settings(
    *,
    settings_path: str | Path | None = None,
    env_prefix: str | None = None,
) -> BaseModel

Role:

  • Initialize process-global source and snapshot.

Parameter behavior:

  • settings_path:
    • explicit bootstrap path.
    • only overrides the initial read of FASTAPIEX__SETTINGS__PATH.
    • does not lock source selection after bootstrap.
  • env_prefix:
    • business env prefix override.
    • None means read from FASTAPIEX__SETTINGS__ENV_PREFIX.
    • non-empty value is treated as raw string prefix and removed from env key names.

Bootstrap source order:

  • settings_path arg
  • FASTAPIEX__SETTINGS__PATH
  • FASTAPIEX__BASE_DIR + /settings.yaml
  • ./settings.yaml

Runtime source switch:

  • after each yaml read, manager checks merged live snapshot key FastAPIEx.settings.path (fastapiex.settings.path) and switches yaml source to that path when changed.
  • this key is treated as an ordinary snapshot key (LWW + priority), not a dedicated control state branch.

Re-init behavior:

  • Re-initializing with a different resolved source raises RuntimeError.

reload_settings

def reload_settings(*, reason: str = "manual") -> BaseModel

Role:

  • Trigger a manual runtime refresh of current settings source.

Behavior:

  • Calls manager-level reload flow.
  • reason is logging context string.

exceptions

from fastapiex.settings import exceptions

Module members:

  • exceptions.SettingsError
  • exceptions.SettingsRegistrationError
  • exceptions.SettingsValidationError
  • exceptions.SettingsResolveError

Runtime Semantics

Registration

  • Only @Settings / @SettingsMap register sections.
  • Undecorated BaseSettings subclasses are ignored by registry discovery.
  • Section paths under FASTAPIEX.* are reserved and rejected (case-insensitive).
  • Registration stores section paths as declared (case-preserving), independent from runtime CASE_SENSITIVE.
  • If multiple declared paths differ only by case, case-insensitive string lookups are ambiguous and fail; type-target lookups remain exact.

Source Loading

Sources:

  • yaml (settings.yaml)
  • .env file
  • process os.environ

Merge strategy:

  • LWW (last-write-wins by source update time)
  • if timestamps tie: env > .env > yaml

Runtime reload:

  • yaml may refresh by mode.
  • .env and process env are loaded once at init time and not re-ingested by runtime yaml reload.
  • when yaml source switches by FastAPIEx.settings.path, only yaml source is switched/reloaded; .env stays from the bootstrap directory.
  • this is intentional by convention: .env is treated as an environment-variable generator, and runtime business env should be stable unless explicitly opted in.

Opt-in .env runtime sync (advanced):

  • This is not part of the package-root public API. Use manager helper explicitly:
from fastapiex.settings import reload_settings
from fastapiex.settings.manager import register_source_sync

# include `.env` in runtime reload passes
register_source_sync("dotenv", sync_on_reload=True)

# optional: also follow `fastapiex.settings.path` source switches
register_source_sync("dotenv", sync_on_path_switch=True)

# optional: force one immediate re-ingest after enabling sync
reload_settings(reason="enable-dotenv-sync")

Parameter meaning:

  • source="dotenv": target .env source behavior.
  • sync_on_reload=True: re-read .env during reload pass (RELOAD=on_change|always, and manual reload_settings).
  • sync_on_path_switch=True: when runtime settings file path switches, also re-read .env from the new settings directory.
  • read_snapshot: normally omitted; keep default reader unless you are replacing source IO behavior.

Monkeypatch / Black Magic

GetSettings / GetSettingsMap expose mutable live objects by design.

Recommended safety flow when manually patching settings at runtime:

  • before monkeypatching, set FastAPIEx.settings.reload (fastapiex.settings.reload) to off.
  • perform the manual mutation.
  • when done and you want to restore canonical values from configured sources, call reload_settings(reason="...").

Runtime Controls (FASTAPIEX__*)

Plain-key behavior:

  • FASTAPIEX__* keys are ingested into live snapshot as plain keys under fastapiex.*.
  • they are readable via GetSettings("fastapiex....").
  • declaration is still forbidden: users cannot declare @Settings("fastapiex...") / @SettingsMap("fastapiex...").
  • mixed-case keys from file/env (for example FastAPIEx.Settings.Reload) are normalized into canonical fastapiex.*.

Supported controls:

  • FASTAPIEX__SETTINGS__PATH
  • FASTAPIEX__BASE_DIR
  • FASTAPIEX__SETTINGS__ENV_PREFIX
  • FASTAPIEX__SETTINGS__CASE_SENSITIVE
  • FASTAPIEX__SETTINGS__RELOAD

FASTAPIEX__SETTINGS__ENV_PREFIX rules:

  • cannot start with FASTAPIEX__.
  • empty prefix means plain keys are considered business keys.
  • non-empty prefix is stripped as raw string.
  • prefix matching follows runtime case mode: case-insensitive when CASE_SENSITIVE=false, exact when CASE_SENSITIVE=true.

Equivalent examples (all readable by GetSettings("one")):

  • FASTAPIEX__SETTINGS__ENV_PREFIX=SOME_CUSTOM_PREFIX__ + SOME_CUSTOM_PREFIX__ONE=1
  • FASTAPIEX__SETTINGS__ENV_PREFIX=SOME_CUSTOM_PREFIX_ + SOME_CUSTOM_PREFIX_ONE=1
  • FASTAPIEX__SETTINGS__ENV_PREFIX=SOME_CUSTOM_PREFIX + SOME_CUSTOM_PREFIXONE=1
  • empty prefix + ONE=1

Env split semantics:

  • delimiter is __
  • FOO___BAR -> ["FOO", "_BAR"]
  • keys that produce empty segments are ignored (for example FOO____BAR, __X, X__)

Case Sensitivity

Control:

  • FASTAPIEX__SETTINGS__CASE_SENSITIVE=true|false (default false)

Behavior:

  • false: case-insensitive lookup for read/env business keys.
  • true: case-sensitive.
  • Windows (os.name == "nt"): true is ignored and behaves as false.
  • FastAPIEx / FastAPIEx.* lookup is always case-insensitive (independent from CASE_SENSITIVE).
  • When false, if sibling keys collapse to the same folded name (for example APP and app), string-path lookup is treated as ambiguous and raises resolve error.

Reload Mode

Control:

  • FASTAPIEX__SETTINGS__RELOAD=off|on_change|always (default off)

Behavior:

  • off: no auto yaml sync.
  • on_change: sync yaml when file state changes.
  • always: sync yaml on each read.
  • module delta changes still trigger declaration rediscovery/snapshot rebuild.
  • runtime controls are resolved from current live snapshot (not direct env re-read).

Intentionally Not Public at Package Root

These capabilities still exist internally, but are not exported from fastapiex.settings:

  • manager/registry direct access helpers

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fastapiex_settings-0.0.2.tar.gz (97.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapiex_settings-0.0.2-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file fastapiex_settings-0.0.2.tar.gz.

File metadata

  • Download URL: fastapiex_settings-0.0.2.tar.gz
  • Upload date:
  • Size: 97.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapiex_settings-0.0.2.tar.gz
Algorithm Hash digest
SHA256 304be61ce4c51d4b2912b1d3fb882a45b652e3fcd5307d8023ab704ca208f84d
MD5 b31b4cbde0a5ccd43f654023f92d6dcc
BLAKE2b-256 6c9e590032a300a7eaaa763bc56c6d6adb7892abb9a78a90a147441cf17cf8bb

See more details on using hashes here.

File details

Details for the file fastapiex_settings-0.0.2-py3-none-any.whl.

File metadata

  • Download URL: fastapiex_settings-0.0.2-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.10.7 {"installer":{"name":"uv","version":"0.10.7","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for fastapiex_settings-0.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d1414f22faeb6cc8dd948c09ceaef30628fb82e3206367f9095d319382461ec2
MD5 118002a60721ee551797d200367698eb
BLAKE2b-256 a81dc01058202d3a05ae48e6a5d2d22179a0410398a025bacf13c0f95bbddd8c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page