Metadata-Version: 2.5
Name: pydantic-versions
Version: 1.0.0
Summary: Bring version control and history to your Pydantic schemas
Project-URL: Homepage, https://github.com/dariuszpanas/pydantic-versions
Project-URL: Documentation, https://pydantic-versions.readthedocs.io/en/latest/
Project-URL: Repository, https://github.com/dariuszpanas/pydantic-versions
Project-URL: Issues, https://github.com/dariuszpanas/pydantic-versions/issues
Author: Dariusz Panas
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: migrations,pydantic,schema-versioning,schemas,validation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: annotated-types>=0.6.0
Requires-Dist: pydantic<3.0,>=2.12.3
Requires-Dist: typing-extensions>=4.14.1
Description-Content-Type: text/markdown

# pydantic-versions

Bring version control and history to your Pydantic schemas.

<p align="center">
  <a href="https://github.com/dariuszpanas/pydantic-versions/actions/workflows/ci.yml"><img src="https://github.com/dariuszpanas/pydantic-versions/actions/workflows/ci.yml/badge.svg?branch=main" alt="CI status"></a>
  <a href="https://pypi.org/project/pydantic-versions/"><img src="https://img.shields.io/pypi/v/pydantic-versions.svg" alt="PyPI version"></a>
  <a href="https://pypi.org/project/pydantic-versions/"><img src="https://img.shields.io/pypi/pyversions/pydantic-versions.svg" alt="Supported Python versions"></a>
  <a href="https://pydantic-versions.readthedocs.io/en/latest/"><img src="https://img.shields.io/readthedocs/pydantic-versions/latest.svg" alt="Documentation status"></a>
  <a href="https://github.com/dariuszpanas/pydantic-versions/blob/main/LICENSE"><img src="https://img.shields.io/github/license/dariuszpanas/pydantic-versions" alt="License"></a>
</p>

<p align="center">
  <img src="https://raw.githubusercontent.com/dariuszpanas/pydantic-versions/main/docs/assets/images/pydantic-versions-hero-transparent.png" alt="pydantic-versions logo" width="760">
</p>

`pydantic-versions` lets projects register ordered schema versions, derive historical Pydantic models from a current model, validate historical payloads, render historical config shapes, and upgrade data to the current model.

## Install

```bash
pip install pydantic-versions
```

With `uv`:

```bash
uv add pydantic-versions
```

## Example

Schema versions are independent from software versions. A config payload can declare
the schema it uses, and the latest software can still validate and upgrade it:

<!-- pv-doc-test: readme-example -->
```python
from pydantic import BaseModel
from pydantic_versions import (
    SchemaFamily,
    SchemaVersion,
    field_default,
    field_removed,
)


class AppConfig(BaseModel):
    timeout: float = 10.0
    retries: int = 3
    new_feature: bool = False


APP_CONFIG_SCHEMA = SchemaFamily(
    model=AppConfig,
    name="app_config",
    versions=(
        SchemaVersion(
            "1",
            patches=(
                field_default("timeout", 5.0),
                field_removed("new_feature"),
            ),
        ),
        SchemaVersion("2"),
    ),
    missing_version="1",
)


result = APP_CONFIG_SCHEMA.validate({"schema_version": "1", "retries": 2})
assert result.current_model == AppConfig(timeout=5.0, retries=2, new_feature=False)

v1_config = APP_CONFIG_SCHEMA.defaults_for(version="1")
assert v1_config == {"timeout": 5.0, "retries": 3, "schema_version": "1"}
```

`defaults_for()` constructs the requested wire version directly, so historical
defaults do not depend on a downgrade route from the current version. Use
`dump(version=..., data=current_config)` when converting actual current data.

`missing_version` is only for legacy config files that do not contain a schema
version field. For example, `missing_version="1"` means "if a payload has no
`schema_version`, treat it as an old v1 config." If you do not set it,
unversioned input raises `MissingSchemaVersionError`.

The current model remains ordinary; its growing history can live in another
module. The decorators remain available as a compact compatibility style. The
docs include external-family guidance, a larger nested config example, and
adoption guidance for choosing patches, migrations, metadata, and legacy
unversioned fallbacks.

## Development

Install dependencies:

```bash
uv sync
```

Run the main checks:

```bash
make ci
```

Useful commands:

- `make format`: format with Ruff.
- `make lint`: lint and auto-fix with Ruff.
- `make typecheck`: run `ty` plus the external mypy consumer contract.
- `make dead-code`: scan production code with Vulture and reviewed local exceptions.
- `make test`: run pytest.
- `make docs-build`: build the docs site.
