Metadata-Version: 2.4
Name: fastfoundry-settings
Version: 2.0.0
Summary: Pydantic-settings building blocks for the fastfoundry ecosystem.
Project-URL: Homepage, https://github.com/Drozdetskiy/fastfoundry-settings
Project-URL: Source, https://github.com/Drozdetskiy/fastfoundry-settings
Project-URL: Changelog, https://github.com/Drozdetskiy/fastfoundry-settings/releases
Project-URL: Issues, https://github.com/Drozdetskiy/fastfoundry-settings/issues
Author-email: Mikhail Drozdetskiy <m.drozdetskiy@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: config,fastapi,fastfoundry,pydantic,pydantic-settings,settings
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: Pydantic
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pydantic-settings[aws-secrets-manager]<3,>=2.5
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: python-dotenv>=1
Description-Content-Type: text/markdown

# fastfoundry-settings

Pydantic-settings building blocks for the **fastfoundry** ecosystem — opinionated,
typed defaults so every FastAPI service configures settings the same way.

Part of the `fastfoundry.*` namespace (`fastfoundry-postgres`, `fastfoundry-redis`, …).

## Install

```bash
pip install fastfoundry-settings
```

Requires Python 3.12+. `boto3` (for the AWS Secrets Manager source) ships as a dependency.

## Usage

```python
from fastfoundry.settings import BaseAppSettings


class Settings(BaseAppSettings):
    project: str = "billing"
    debug: bool = False


settings = Settings()
```

Set values through `FF_`-prefixed environment variables (or a `.env` / `.env.local` file) —
e.g. `FF_DEBUG=true`.

`BaseAppSettings` ships these `model_config` defaults (override `model_config` to change any):

| Setting | Value | Effect |
| --- | --- | --- |
| `env_prefix` | `FF_` | env vars are read under the `FF_` prefix (override per app) |
| `env_file` | `(".env", ".env.local")` | loads local env files if present (`.env.local` overrides) |
| `env_nested_delimiter` | `__` | `FF_DB__HOST` populates `settings.db.host` |
| `case_sensitive` | `False` | `FF_DEBUG` and `ff_debug` both match |
| `extra` | `allow` | unknown env vars are kept rather than raising |

It also declares the fields that drive AWS Secrets Manager (below): `use_aws_secrets`,
`region`, and `project`.

## Escaped secrets

`EscapedSecretStr` is a `SecretStr | None` that restores literal `\n`/`\t` to real characters
before wrapping — handy for a PEM or JWT key supplied on a single line. `""` and `None`
become `None`.

```python
from fastfoundry.settings import BaseAppSettings, EscapedSecretStr


class Settings(BaseAppSettings):
    jwt_private_key: EscapedSecretStr = None
```

## AWS Secrets Manager

Built on pydantic-settings' own `AWSSecretsManagerSettingsSource` (`boto3` ships with the
package, so it works out of the box):

```python
from fastfoundry.settings import BaseAppSettings


class Settings(BaseAppSettings):
    project: str = "myservice"
    region: str | None = "eu-central-1"   # optional; defaults to boto3's resolution

    database_dsn: str | None = None
    api_key: str | None = None


# FF_USE_AWS_SECRETS=true -> loads the secret named "myservice"
settings = Settings()
```

- The source is added only when `use_aws_secrets` resolves truthy (set `FF_USE_AWS_SECRETS=true`,
  or pass it to the constructor).
- The secret id comes from `aws_secret_id(cls, resolved) -> str` — `project` by default. Override
  it for a richer scheme (e.g. an environment-prefixed `"{env}/{project}"`). The `SecretString`
  is a JSON object whose keys have the `FF_` prefix stripped and match fields case-insensitively.
- It sits **just below init args and above the environment** — a secret overrides an env var,
  while an explicit constructor argument still wins. Precedence (highest first): init, AWS,
  environment, `.env`, file secrets.
- Secret values equal to `"REPLACE_ME"` or `""` are **dropped**, so a Terraform-seeded
  placeholder falls through to a lower source instead of impersonating a real value.

## Settings singleton

Build one settings object at start-up and read it anywhere. `set_cfg` takes the settings
*class* and builds it (forwarding `**kwargs`); services wrap `base_cfg` in a typed accessor:

```python
from typing import cast

from fastfoundry.settings import BaseAppSettings, base_cfg, set_cfg


class Settings(BaseAppSettings):
    project: str = "myservice"
    debug: bool = False


def cfg() -> Settings:
    return cast("Settings", base_cfg())


def main() -> None:
    set_cfg(Settings)        # build + register once, at start-up
    if cfg().debug:          # read the typed singleton anywhere
        ...
```

- `set_cfg(Settings, **kwargs)` — build the class (with optional init overrides) and register
  it as the process-wide singleton; a later call replaces it.
- `base_cfg()` — return the registered instance (raises `RuntimeError` if `set_cfg` has not run).

Not thread-safe; register during single-threaded start-up.

## Development

```bash
mise run install     # uv sync --dev
mise run lint        # ruff check
mise run typecheck   # mypy --strict
mise run test        # pytest
mise run build       # uv build (sdist + wheel)
```

Versions are derived from git tags (hatch-vcs) and released automatically by
semantic-release on `main`; nothing is hand-written into `pyproject.toml`.

## License

MIT
