Metadata-Version: 2.4
Name: wirio-settings
Version: 0.5.0
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.13
Classifier: Intended Audience :: Developers
Requires-Dist: pydantic>=2.13.4
License-File: LICENSE
Summary: Lightning-fast, strongly typed, and zero-boilerplate settings library for Python
Keywords: settings,azure,Azure Key Vault,aws,AWS Secrets Manager,gcp,GCP Secret Manager,kubernetes,Kubernetes CSI
Author: Andreu Codina
License-Expression: MIT
Requires-Python: >=3.13
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Changelog, https://github.com/wirio-org/wirio/releases-settings
Project-URL: Documentation, https://wirio-org.github.io/wirio-settings
Project-URL: Homepage, https://github.com/wirio-org/wirio-settings
Project-URL: Repository, https://github.com/wirio-org/wirio-settings

<div align="center">
<img alt="Logo" src="https://raw.githubusercontent.com/wirio-org/wirio-settings/refs/heads/main/docs/logo.png" width="450" height="450">

[![CI](https://img.shields.io/github/actions/workflow/status/wirio-org/wirio-settings/ci.yaml?branch=main&logo=github&label=CI)](https://github.com/wirio-org/wirio-settings/actions/workflows/ci.yaml)
[![PyPI - version](https://img.shields.io/pypi/v/wirio-settings?color=blue&label=pypi)](https://pypi.org/project/wirio-settings/)
[![Python - versions](https://img.shields.io/pypi/pyversions/wirio-settings.svg)](https://github.com/wirio-org/wirio-settings)
[![License](https://img.shields.io/github/license/wirio-org/wirio-settings.svg)](https://github.com/wirio-org/wirio-settings/blob/main/LICENSE)

</div>

## Overview

Lightning-fast, strongly typed, and zero boilerplate settings library for Python:

- **Great defaults from day one:** It automatically looks for settings files and environment variables, with recommended configurations and one line of code.
- **Rust-powered core:** Built with Rust under the hood for speed, reliability, and low runtime overhead.
- **Secret stores:** Azure Key Vault, AWS Secrets Manager and GCP Secret Manager integrations are available with one line of code, with safe authentication.
- **Pydantic models:** Load your application settings directly into models.
- **Automatic reloads:** Keep settings up to date by automatically reloading them.
- **A practical replacement:** Replace `pydantic-settings` and `python-dotenv` with one unified settings library.
- **Roadmap:** Planned capabilities include pluggable configuration stores, feature flags, lifetimes, prefixes, filters, custom delimiters and aliases.

## Table of contents

- [Overview](#overview)
- [Table of contents](#table-of-contents)
- [📦 Installation](#-installation)
- [✨ Quickstart with fixed strings](#-quickstart-with-fixed-strings)
- [✨ Quickstart with Pydantic models](#-quickstart-with-pydantic-models)
- [✨ Quickstart with Pydantic models and Azure Key Vault](#-quickstart-with-pydantic-models-and-azure-key-vault)
- [All providers](#all-providers)
  - [YAML file](#yaml-file)
  - [JSON file](#json-file)
  - [Environment variables](#environment-variables)
  - [Azure Key Vault](#azure-key-vault)
  - [AWS Secrets Manager](#aws-secrets-manager)
  - [GCP Secret Manager](#gcp-secret-manager)
  - [Key-per-file directory](#key-per-file-directory)
- [Configuration](#configuration)
  - [Provider priority](#provider-priority)
  - [Naming convention](#naming-convention)
  - [Recommended usage](#recommended-usage)
- [Reading settings](#reading-settings)
  - [Read one value](#read-one-value)
  - [Defaults and required fields](#defaults-and-required-fields)
  - [Sections](#sections)
  - [Nested keys](#nested-keys)
  - [Pydantic model reloads](#pydantic-model-reloads)
  - [Debug settings](#debug-settings)

## 📦 Installation

```bash
uv add wirio-settings
```

## ✨ Quickstart with fixed strings

```python
from wirio_settings import SettingsManager


settings_manager = SettingsManager()
database_password = settings_manager.get_required_value("database_password")
```

## ✨ Quickstart with Pydantic models

```python
from pydantic import BaseModel
from wirio_settings import SettingsManager


class ApplicationSettings(BaseModel):
    database_password: str


application_settings = SettingsManager().get_model(ApplicationSettings)
```

## ✨ Quickstart with Pydantic models and Azure Key Vault

```python
from pydantic import BaseModel
from wirio_settings import SettingsManager


class ApplicationSettings(BaseModel):
    database_password: str


application_settings = (
    SettingsManager()
    .add_azure_key_vault("https://example.vault.azure.net/")
    .get_model(ApplicationSettings)
)
```

## All providers

### YAML file

```python
settings_manager.add_yaml_file("file.yaml")
```

Comments are supported in YAML files.

Options:

- `optional=True` skips the file if it is missing. The file is required by default.
- `reload_on_change=True` reloads values when the file changes.

### JSON file

```python
settings_manager.add_json_file("file.json")
```

Comments are not supported in JSON files.

Options:

- `optional=True` skips the file if it is missing. The file is required by default.
- `reload_on_change=True` reloads values when the file changes.

### Environment variables

```python
settings_manager.add_environment_variables()
```

### Azure Key Vault

```python
settings_manager.add_azure_key_vault(
    "https://example.vault.azure.net",
)
```

If no explicit credentials are provided, `DefaultAzureCredential` is used.

`DefaultAzureCredential` tries credentials in this order and uses the first one that succeeds:

1. Environment credential (`AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, `AZURE_TENANT_ID`)
2. Workload identity credential
3. Developer tools credential (Azure CLI / Azure Developer CLI)
4. Managed identity credential. This is the System-assigned managed identity by default. If we want to use a User-assigned managed identity, set the `AZURE_CLIENT_ID` environment variable.

If you want to use explicit service principal credentials, provide all three values:

```python
settings_manager.add_azure_key_vault(
    "https://example.vault.azure.net",
    client_id="...",
    client_secret="...",
    tenant_id="...",
)
```

When using explicit credentials, `tenant_id`, `client_id`, and `client_secret` must all be provided.

> [!NOTE]
> **Azure permissions:** Usually, the `Key Vault Secrets User` role is used to read secrets.

To periodically refresh the loaded secrets, use the `reload_interval` parameter. The provider waits that long between refresh attempts and keeps the last successfully loaded settings if a refresh fails.

```python
from datetime import timedelta


settings_manager.add_azure_key_vault(
    "https://example.vault.azure.net",
    reload_interval=timedelta(minutes=5),
)
```

### AWS Secrets Manager

```python
settings_manager.add_aws_secrets_manager(
    "secret-id",
)
```

The secret value must be a JSON object. `wirio-settings` reads and flattens that JSON into settings keys.

By default, the provider uses the [credential provider chain](https://docs.aws.amazon.com/sdk-for-rust/latest/dg/credproviders.html#credproviders-default-credentials-provider-chain). For example, the IAM role, the shared AWS configuration profile, or `AWS_*` environment variables.

If explicit credentials are provided, they override environment authentication for this provider instance:

```python
settings_manager.add_aws_secrets_manager(
    secret_id="secret-id",
    access_key_id="...",
    secret_access_key="...",
)
```

### GCP Secret Manager

```python
settings_manager.add_gcp_secret_manager("project-id")
```

If no credentials are provided, [Application Default Credentials (ADC)](https://docs.cloud.google.com/docs/authentication/application-default-credentials) are used.
We can also pass custom GCP credentials with the `credentials_json` parameter.

### Key-per-file directory

```python
settings_manager.add_key_per_file("secrets")
```

Given a directory, each file name becomes a setting key and the file content becomes the setting value.

Options:

- `optional=True` skips the directory if it is missing. The directory is required by default.
- `reload_on_change=True` reloads values when directory contents change.

This provider is useful when secrets are mounted as files by the runtime instead of exposed as environment variables. It lets us keep application code unchanged while switching the secret delivery mechanism.

Common use cases:

- Kubernetes with [Secrets Store CSI Driver](https://secrets-store-csi-driver.sigs.k8s.io/) where providers such as Azure Key Vault mount each secret as a file.
- Docker/Kubernetes secret mounts (for example, `/run/secrets`).
- Platform-managed secret volumes in production environments where file-based delivery is preferred.

Example directory:

```
secrets/
    database_password
    openai_api_key
```

Then values are available as `database_password` and `openai_api_key`.

## Configuration

### Provider priority

`wirio-settings` supports multiple providers. When the same key exists in multiple providers, the last added providers have more priority.

The following providers are loaded, by default, in this order:

1. `settings.yaml`
2. `settings.{environment}.yaml`.
3. Environment variables

Considerations:

- Files are optional. If a file is not found, it's skipped.
- `{environment}` is the value of the `WIRIO_ENVIRONMENT` environment variable. If the variable is not set, its value is `local`. This would load, for example, `settings.production.yaml` if `WIRIO_ENVIRONMENT=production`. It standardizes the environment detection and allows us to store all settings in code, with version control.
- In the default settings, environment variables have higher priority than YAML files because the provider is added after them. This means that if a key exists in both `settings.yaml` and environment variables, the value from environment variables will be used.
- If we add more providers, those will have higher priority than the defaults. For example, if we add Azure Key Vault as a provider, it will override the defaults.

  ```python
  SettingsManager().add_azure_key_vault("https://example.vault.azure.net/")
  ```

### Naming convention

Each provider (environment variables, YAML, Azure Key Vault...) has its own naming convention for keys. `wirio-settings` uses snake case for settings keys. When loading from providers, keys are normalized to snake case. For example, the `APP_NAME` environment variable maps to `app_name`.

### Recommended usage

It depends on your usage, but the recommended setup is having the following files:

- `settings.yaml` with the shared settings.
- `settings.{environment}.yaml` with the environment-specific settings. For example, `settings.production.yaml`, `settings.staging.yaml`, `settings.local.yaml`, etc.

Then, we declare the settings manager, loading the default providers:

```python
settings_manager = SettingsManager()
```

Now, `settings_manager` has settings loaded.

Let's say our API is deployed in production, so we have read the `key_vault_url` setting from `settings.production.yaml`. We can now add Azure Key Vault as a provider and read the rest of the settings from there:

```python
settings_manager.add_azure_key_vault(
    settings_manager.get_required_value("key_vault_url")
)
```

After that, we have all settings to construct our Pydantic model:

```python
application_settings = settings_manager.get_model(ApplicationSettings)
```

## Reading settings

### Read one value

- Use `get_required_value` when the key must exist.

```python
openai_api_key = settings_manager.get_required_value("openai_api_key")
```

By default, the settings system returns values as strings. To validate and convert to another type, pass the type as a second argument.

```python
timeout_seconds = settings_manager.get_required_value("maximum_retries", int)
```

- Use `get_value` for optional keys.

```python
openai_api_key = settings_manager.get_value("openai_api_key")
timeout_seconds = settings_manager.get_value("maximum_retries", int)
```

### Defaults and required fields

If a model field has a default, that default is used when no value is found.

```python
from pydantic import BaseModel


class ApplicationSettings(BaseModel):
    app_name: str
    port: int | None = None
```

Here, `port` defaults to `None` when missing.
If a required field is missing, `get_model` raises `KeyError`.

### Sections

Use `get_section` to read a section. For example, we can read the next YAML:

```yaml
logging:
  log_level: WARNING
```

```python
log_level = settings_manager.get_section("logging").get_required_value("log_level")
```

`SettingsSection` supports:

- The section value itself with `section.get_required_value()` or `section.get_required_value(type)`.
- A child value with `section.get_required_value("child.key")` or `section.get_required_value("child.key", type)`.

If a section has only children and no value at its own path, `section.get_value()` returns `None`.

### Nested keys

Nested keys use `.`:

- `database.host`
- `database.port`
- `logging.log_level.default`

### Pydantic model reloads

Models returned by `get_model()` are automatically updated when a configured provider reloads its values. This is useful for long-running applications such as web servers or background jobs that need to keep their settings up to date without restarting or redeploying.

```python
from pydantic import BaseModel
from wirio_settings import SettingsManager


class ApplicationSettings(BaseModel):
    port: int


application_settings = (
    SettingsManager()
    .add_yaml_file("settings.yaml", reload_on_change=True)
    .get_model(ApplicationSettings)
)
```

When `settings.yaml` changes its contents, `application_settings.port` is updated without calling `get_model()` again. If the refreshed values do not validate against the model, the existing model values are retained.

### Debug settings

Use `debug_repr()` to inspect settings and their providers. When several providers contain the same key, the value from the provider with the highest priority is shown.

```python
print(settings_manager.debug_repr())
```

