Metadata-Version: 2.4
Name: pydantic-12-shims
Version: 0.0.1
Summary: Shims for pydantic 1 to use syntax like pydantic 2.
Author-email: Lucas Wiman <lucas.wiman@akasa.com>
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: pydantic
Description-Content-Type: text/markdown

# pydantic-12-shims

A compatibility layer that lets you write Pydantic v2-style code that works with both Pydantic v1 and v2.

## Problem

Pydantic v2 introduced significant API changes, outlined in their [migration guide](https://docs.pydantic.dev/latest/migration/). Key changes include:

| Pydantic v1 | Pydantic v2 |
|-------------|-------------|
| `.parse_obj()` | `.model_validate()` |
| `.dict()` | `.model_dump()` |
| `.json()` | `.model_dump_json()` |
| `class Config` | `model_config = ConfigDict(...)` |
| `@validator` | `@field_validator` |
| `@root_validator` | `@model_validator` |

This library provides shims so you can write v2-style code that runs on both versions.

## Why use this library?

In a heterogeneous environment where some projects use Pydantic v1 and others v2, this library lets you maintain a single codebase with v2-style syntax. You can incrementally migrate while still on `pydantic<2` — ideally, the final change is just updating a `pyproject.toml`. Once all services use Pydantic v2, you can replace `pydantic_12_shims` imports with direct Pydantic v2 imports.

Pydantic v2 provides the `pydantic.v1` module for this purpose, but `pydantic.v1.BaseModel` is *not* interoperable with native v2 `BaseModel`. For example, using a `pydantic.v1` model as a field type in a native v2 model raises a `TypeError`:

```python
# module1.py is still stuck on pydantic.v1:
from pydantic.v1 import BaseModel as V1BaseModel, Field

class MyLegacyModel(V1BaseModel):
    name: str = Field(default="legacy")


# module2.py wants to use native v2 models:
from pydantic import BaseModel
from module1 import MyLegacyModel

class ShinyNewModel(BaseModel):
    legacy_value: MyLegacyModel
    # TypeError: BaseModel.validate() takes 2 positional arguments but 3 were given
```

This led major libraries like FastAPI to drop `pydantic.v1` support entirely, making it an unviable migration path.


## Installation

```bash
pip install pydantic-12-shims
```

## Usage

Replace your Pydantic imports with imports from this library:

```python
# Instead of:
# from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict

# Use:
from pydantic_12_shims import BaseModel, Field, field_validator, model_validator, ConfigDict
```

Then write your models using Pydantic v2 syntax:

```python
from pydantic_12_shims import BaseModel, ConfigDict, Field, field_validator, model_validator

class User(BaseModel):
    model_config = ConfigDict(frozen=True, populate_by_name=True)

    name: str = Field(min_length=1)
    email: str
    age: int = Field(ge=0)

    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        if "@" not in v:
            raise ValueError("Invalid email")
        return v.lower()

    @model_validator(mode="after")
    def validate_model(self):
        # validation logic
        return self

# Use v2-style methods
user = User.model_validate({"name": "Alice", "email": "ALICE@example.com", "age": 30})
data = user.model_dump()
json_str = user.model_dump_json()
```

This code works identically whether Pydantic v1 or v2 is installed.

## API Reference

### Exports

- `BaseModel` - Base class for models with v2-style methods
- `ConfigDict` - Configuration dictionary (v2 style)
- `Field` - Field definition with v2 parameter names
- `field_validator` - Field validator decorator (v2 style)
- `model_validator` - Model validator decorator (v2 style)
- `GenericModel` - Base class for generic models
- `ValidationError` - Validation exception
- `PrivateAttr` - Private attribute marker
- `PYDANTIC1` / `PYDANTIC2` - Booleans indicating which major version is installed

### Validators

**field_validator**:
```python
@field_validator("field_name", mode="before")  # or mode="after"
@classmethod
def validate_field(cls, v):
    return v
```

**model_validator**:
```python
@model_validator(mode="before")  # receives dict
@classmethod
def validate_before(cls, values: dict) -> dict:
    return values

@model_validator(mode="after")  # receives model instance
def validate_after(self):
    return self
```

Note: support for validators is partial, e.g. the `handler` and `info` arguments are not yet supported.

## Generic Models

For generic models, use `GenericModel`:

```python
from typing import Generic, TypeVar
from pydantic_12_shims import GenericModel

T = TypeVar("T")

class Container(GenericModel, Generic[T]):
    value: T

# Parameterize the generic
IntContainer = Container[int]
instance = IntContainer(value=42)
```

## Migrating off `pydantic.v1`

If your codebase uses Pydantic v2's `pydantic.v1` compatibility module and you want to migrate away from it:

```python
# Instead of:
# from pydantic.v1 import BaseModel

# Use:
from pydantic_12_shims.v1 import BaseModel
```

On Pydantic v2, `pydantic_12_shims.v1` wraps `pydantic.v1` models to be interoperable with native v2 `BaseModel`. Once every `pydantic.v1` import has been replaced, set the environment variable `TURN_PYDANTIC_V1_OFF=true` to switch to native v2 models entirely — this lets you verify the migration works before removing the shim imports.

## Version Detection

```python
from pydantic_12_shims import PYDANTIC1, PYDANTIC2

if PYDANTIC1:
    # Pydantic v1 specific code
    pass

if PYDANTIC2:
    # Pydantic v2 specific code
    pass
```

## License

See LICENSE file.
