Metadata-Version: 2.3
Name: pytastic
Version: 0.6.0
Summary: A zero-dependency JSON validation library using TypedDict and Annotated (optional orjson acceleration)
Author: Tersoo
Author-email: tersoo@example.com
Requires-Python: >=3.9,<4.0
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Provides-Extra: fast
Requires-Dist: orjson (>=3.9,<4.0) ; extra == "fast"
Project-URL: Repository, https://github.com/rayattack/pytastic
Description-Content-Type: text/markdown

# Pytastic

**No Magic. Just Python.**

Pytastic validates JSON-shaped data against schemas you write as ordinary `TypedDict`
type hints. If you know `TypedDict` and `Annotated`, you already know how to use it.
Zero dependencies, and your validated data stays a plain `dict`.

Full documentation: [rayattack.github.io/pytastic](https://rayattack.github.io/pytastic/)

```python
from typing import TypedDict, Annotated, Literal
from pytastic import Pytastic, ValidationError

vx = Pytastic()

class User(TypedDict):
    username: Annotated[str, "min_len=3; regex=^[a-z_]+$"]
    age: Annotated[int, "min=18"]
    role: Literal["admin", "user"]

user = vx.validate(User, {"username": "tersoo", "age": 25, "role": "admin"})

try:
    vx.validate(User, {"username": "x", "age": 25, "role": "admin"})
except ValidationError as e:
    print(e.errors)   # [{'path': '.username', 'message': 'Min length 3'}]
```

Constraints are semicolon-separated `key=value` pairs inside the `Annotated` metadata
string. See the [constraint reference](https://rayattack.github.io/pytastic/syntax/).

## Why?

- **Zero dependencies.** Pure standard library, so it installs anywhere a compiled wheel
  is awkward. `orjson` is an optional accelerator.
- **Your data stays a dict.** Validation returns the dict you passed in, typed as your
  own `TypedDict` — no model objects, no `.model_dump()` at every boundary.
- **No learning curve.** Standard `typing` constructs your editor already understands.
- **Fast where it counts.** See below.

## Performance

Same six-field schema, same constraints, Python 3.12, best of 5 runs. Reproduce with
`python benchmark.py`:

| Comparison | Result |
|---|---|
| vs Pydantic `BaseModel(**kwargs)` | Pytastic ~1.5x faster |
| vs Pydantic `model_validate(dict)` | Pytastic ~1.4x faster |
| vs Pydantic `TypeAdapter(TypedDict)` | **Pydantic ~1.3x faster** |
| JSON bytes → validated | **Pydantic ~2x faster** (fused parse in Rust) |
| Memory, 10,000 records | Pytastic allocates nothing; Pydantic ~4.7 MiB |

To be straight about it: Pytastic beats Pydantic's `BaseModel` paths and loses to
`TypeAdapter` and to Pydantic's fused JSON parsing, which is compiled Rust. If raw
throughput on large JSON payloads is your only concern, use `msgspec`. Pytastic's case is
being fast *and* dependency-free *and* allocation-free while your data stays a dict.

## Installation

```bash
pip install pytastic
```

With optional `orjson` acceleration:

```bash
pip install pytastic[fast]
```

Requires Python 3.9 or newer.

## Two ways to call it

**Typed** — no registration, best for editor autocompletion:

```python
user = vx.validate(User, data)
```

**Dynamic** — register once, then call the schema by name. Skips option handling, so it
is marginally faster in a hot loop, but accepts no options:

```python
vx.register(User)
user = vx.User(data)
```

Registering at startup also compiles the schema immediately, so a malformed schema fails
at boot rather than on the first request.

## JSON Schema export

```python
print(vx.schema(User))
# {"type": "object", "properties": {"username": {"type": "string", "minLength": 3, ...}}, ...}
```

Returns a JSON **string** (Draft 2020-12). Use `json.loads()` if you need a dict.

## Beyond the basics

Pytastic also supports partial/PATCH validation, unknown-field stripping, input and
output field mapping, defaults, computed fields, pre- and post-validation hooks, dotted
attribute access and conditional constraints. See
[Advanced Usage](https://rayattack.github.io/pytastic/advanced/).

## Known limitations

Worth knowing before you adopt:

- **One error per call.** Validation stops at the first failure rather than collecting
  every problem, unlike Pydantic.
- **No rich types.** `datetime`, `UUID` and `Decimal` are not validated natively; use a
  `str` field with `format=` plus a `getter=` hook to hydrate.
- **No recursive schemas.** A self-referential `TypedDict` raises `SchemaDefinitionError`.
- **Validation mutates its input** unless you pass `copy=True`.

## License

MIT

