Metadata-Version: 2.4
Name: fastfoundry-settings
Version: 1.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: loguru<0.8,>=0.7
Requires-Dist: pydantic-settings<3,>=2.3
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: python-dotenv>=1
Provides-Extra: aws
Requires-Dist: botocore<2,>=1.34; extra == 'aws'
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
# or
uv add fastfoundry-settings
```

Requires Python 3.12+.

## Usage

```python
from fastfoundry.settings import BaseAppSettings


class Settings(BaseAppSettings):
    service_name: str
    debug: bool = False


settings = Settings()
```

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

`BaseAppSettings` ships these 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` | loads variables from a local `.env` if present |
| `env_nested_delimiter` | `__` | `FF_DB__HOST` populates `settings.db.host` |
| `case_sensitive` | `False` | `FF_DEBUG` and `ff_debug` both match |
| `extra` | `ignore` | unknown env vars don't raise |

Short import alias, if you like:

```python
import fastfoundry.settings as ff
ff.BaseAppSettings
```

## Helper field types

Two `Annotated` types for common settings shapes:

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


class Settings(BaseAppSettings):
    # "a,b,c" (or FF_HOSTS=a,b,c) -> ["a", "b", "c"]; a real list passes through.
    hosts: StringList = None
    # literal \n / \t are restored before wrapping in SecretStr; "" / None -> None.
    # handy for a PEM or JWT key supplied on a single line.
    private_key: EscapedSecretStr = None
```

- `StringList` — a `list[str] | None` that also accepts a single comma-separated string.
- `EscapedSecretStr` — a `SecretStr | None` that restores literal `\n`/`\t` to real characters.

## AWS Secrets Manager

Install the extra and point a settings class at a secret:

```bash
pip install 'fastfoundry-settings[aws]'
```

```python
from fastfoundry.settings import BaseAppSettings


class Settings(BaseAppSettings):
    aws_secret_id = "prod/myservice"    # enables the AWS Secrets Manager source
    aws_region_name = "eu-central-1"    # optional; defaults to botocore's resolution

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


settings = Settings()    # database_dsn / api_key come from the secret
```

The secret's `SecretString` must be a JSON object; keys have the `FF_` prefix stripped
(when present) and match fields case-insensitively, and JSON object/array values are
decoded into dicts/lists. The source is the **lowest-precedence** layer, so an explicit
env var overrides a secret value. If the secret can't be fetched (missing, unreachable,
malformed) the source logs a warning and contributes nothing — settings still build from
env and defaults. Without the `aws` extra installed, using it raises a clear `ImportError`.

The `AwsSecretsManagerSource` class is exported too, for composing it into a custom
`settings_customise_sources` yourself.

## Settings singleton

For apps that build one settings object at start-up and read it anywhere:

```python
from fastfoundry.settings import cfg, config_factory, set_cfg


def main() -> None:
    set_cfg(config_factory(Settings))    # build + register once
    run_app()


def run_app() -> None:
    if cfg().debug:                      # read the singleton anywhere
        ...
```

- `config_factory(Settings, **kwargs)` — build a settings instance.
- `set_cfg(config)` — register the process-wide singleton (once; raises if already set).
- `cfg()` — return it (raises `RuntimeError` if `set_cfg` has not run).

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

## Logging

loguru is the default logger. Configure it once at start-up:

```python
from fastfoundry.settings import LoggingSettings, setup_logging

setup_logging(LoggingSettings(level="INFO"))    # or setup_logging() for the defaults
```

Drive it from the environment by embedding `LoggingSettings` as a field:

```python
from fastfoundry.settings import BaseAppSettings, LoggingSettings, setup_logging


class Settings(BaseAppSettings):
    logging: LoggingSettings = LoggingSettings()


settings = Settings()               # FF_LOGGING__LEVEL=DEBUG, FF_LOGGING__SERIALIZE=true, …
setup_logging(settings.logging)
```

| Field | Default | Effect |
| --- | --- | --- |
| `level` | `INFO` | minimum level for the `stderr` sink |
| `serialize` | `False` | emit structured JSON lines (set `true` in production) |
| `colorize` | `True` | colored, human-readable output (ignored when `serialize`) |
| `backtrace` / `diagnose` | `False` | loguru exception detail (keep off where secrets may appear) |
| `intercept_stdlib` | `True` | route standard-library `logging` through loguru too |

## 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
