Skip to main content

schemmy

Validate data with plain type annotations. No dependencies.

pip install schemmy
# or
uv add schemmy

Requires Python 3.12+.

Example

from typing import Annotated

from schemmy import Schema, ValidationError


def positive(value: int) -> int:
    if value <= 0:
        raise ValueError("must be greater than zero")
    return value


class Item(Schema):
    sku: str
    qty: Annotated[int, positive]


class Order(Schema):
    id: int
    items: list[Item]


order = Order.validate({"id": 1, "items": [{"sku": "A1", "qty": 2}]})
order.items[0].qty  # 2

All errors are collected at once, not just the first one. Each error carries the path to the field:

try:
    Order.validate({"id": "1", "items": [{"sku": "A1", "qty": 0}, {"qty": 3}], "note": "x"})
except ValidationError as exc:
    for error in exc.exceptions:
        print(error.location, error.message)
['note'] Unknown field
['id'] Expected int, got str
['items', 0, 'qty'] Validator positive failed: must be greater than zero
['items', 1, 'sku'] Field required

Errors come in a stable order: missing and unknown fields first, then value errors in field declaration order.

ValidationError is an ExceptionGroup, so except* works too.

What is supported

Annotation Behavior
int, str, float, bool, any class isinstance check, no type coercion. bool is rejected for int fields
float Also accepts an int that float represents exactly and converts it, since JSON sends 1 for 1.0
Any Value is accepted as is
list[T] Each item is checked against T; the item index is added to the error path
tuple[T, ...], tuple[T1, T2] Accepts a tuple or a list and stores a tuple, since JSON has no tuples. A fixed tuple requires the exact length
dict[K, V] Each key is checked against K and each value against V; the key is added to the error path
Literal[...] The value must equal one of the literals and have the same type, so True does not match Literal[1]
X | Y, Optional[X] Members are tried in order, the first match wins. For X | None, errors of X are reported as is
Schema subclass Accepts a schema instance or a dict
Annotated[T, *validators] T is checked first, then validators run in order

A validator is a callable that takes a value and returns it, possibly changed. To report an error, a validator raises an exception. The returned value is stored in the schema, so validators can normalize data, for example bring a phone number to a single format.

Unknown fields and missing required fields are validation errors as well.

A field with a default value is optional. If the field is not passed, the schema gets a copy of the default. The default value itself is not validated.

ClassVar annotations are not fields.

Working with instances

order.to_dict()
# {'id': 1, 'items': [{'sku': 'A1', 'qty': 2}]}

order
# Order(id=1, items=[Item(sku='A1', qty=2)])

Order.validate(order.to_dict()) == order
# True

to_dict converts nested schemas recursively and keeps field declaration order. Schemas compare by class and field values. Schemas are mutable and therefore unhashable.

Type checking

Type checkers derive the constructor signature from the annotations, so wrong types, unknown and missing fields are reported before runtime:

Item(sku=1, qty=2)  # error: expected str
Item(sku="A1", qyt=2)  # error: unknown argument

The constructor is typed for already typed values. Use Schema.validate for raw data such as parsed JSON, where nested schemas arrive as dicts.

Limitations

set, frozenset, TypedDict and other annotations not listed above are not supported yet. A field with such an annotation produces an "unsupported type" error.

Development

uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .
uv run ty check

License

MIT

Metadata

Release files for schemmy 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for schemmy 0.1.0
File Size Uploaded Provenance Actions
schemmy-0.1.0.tar.gz 9.2 kB Without provenance Details for schemmy-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for schemmy 0.1.0
File Interpreter ABI Platform Actions
schemmy-0.1.0-py3-none-any.whl Python 3 none any Details for schemmy-0.1.0-py3-none-any.whl

Total release size: 18.6 kB

Release files / schemmy-0.1.0.tar.gz

Download URL schemmy-0.1.0.tar.gz
Size 9.2 kB
Tags Source
SHA-256 checksum
How to use checksums
a84ae3725896d2b507dbd166f4da15ffbc87b32177e4b7dde540a5f5a2920642
BLAKE2b-256 checksum
How to use checksums
19de977118c7bd9b172f98ddc929f58e96b500548b61f12403b8f353f61ddfad
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / schemmy-0.1.0-py3-none-any.whl

Download URL schemmy-0.1.0-py3-none-any.whl
Size 9.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
88ab3f92c3d7a45f763288dcc6d528082436b5c6477f7995ffbcf5cff6cc674c
BLAKE2b-256 checksum
How to use checksums
10dd39b0db7e5eaac260fd6e316e2ea0e55a61ef7a59e63f66c9b28e08d5fcb5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.1 {"installer":{"name":"uv","version":"0.13.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for schemmy 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page