Lightweight mixin for generic introspection and Annotated field lookup
Project description
Pydantic Super Model
A lightweight mixin for generic type introspection and Annotated field lookup. Works with any Python class, with optional Pydantic integration.
Two classes:
| Class | Base | Key extras |
|---|---|---|
SuperModelMixin |
Any class | Framework-agnostic annotation introspection |
SuperModelPydanticMixin |
Pydantic BaseModel |
Auto FieldNotImplemented validation, omits unset default None values |
from pydantic_super_model import AnnotatedFieldInfo, FieldNotImplemented, SuperModelMixin, SuperModelPydanticMixin
Installation
pip install pydantic-super-model
Quick Start
With any Python class
from typing import Annotated
from pydantic_super_model import SuperModelMixin
class PrimaryKeyAnnotation:
pass
PrimaryKey = Annotated[int, PrimaryKeyAnnotation]
class User(SuperModelMixin):
id: PrimaryKey
name: str
def __init__(self, id: PrimaryKey, name: str) -> None:
self.id = id
self.name = name
user = User(id=1, name="John Doe")
field_info = user.get_annotated_fields(PrimaryKey)["id"]
assert field_info.value == 1
assert field_info.annotation == PrimaryKey
assert field_info.metadata == (PrimaryKeyAnnotation,)
With Pydantic
from typing import Annotated
from pydantic_super_model import SuperModelPydanticMixin
class PrimaryKeyAnnotation:
pass
PrimaryKey = Annotated[int, PrimaryKeyAnnotation]
class User(SuperModelPydanticMixin):
id: PrimaryKey
name: str
user = User(id=1, name="John Doe")
field_info = user.get_annotated_fields(PrimaryKey)["id"]
assert field_info.value == 1
assert field_info.annotation == PrimaryKey
assert field_info.metadata == (PrimaryKeyAnnotation,)
API
get_annotated_fields(*annotations)
Return a dictionary of field names to AnnotatedFieldInfo for fields whose type hints carry any requested annotation.
- Match by the full
Annotated[...]alias or by metadata type - Include falsy values such as
0
SuperModelPydanticMixin only: unset default None values are omitted. Explicitly provided None is included.
class UserOptional(SuperModelPydanticMixin):
id: PrimaryKey | None = None
name: str
# Unset default None is omitted
assert not UserOptional(name="A").get_annotated_fields(PrimaryKey)
# Explicitly provided None is included
field_info = UserOptional(id=None, name="B").get_annotated_fields(PrimaryKey)["id"]
assert field_info.value is None
SuperModelMixin: all None values are included regardless of whether they were explicitly set.
get_annotated_field_value(annotation, allow_none=False, allow_undefined=False)
Return the first matching AnnotatedFieldInfo.
- Raises
ValueErrorif no matching field exists (unlessallow_undefined=True) - Raises
ValueErrorif the matched value isNone(unlessallow_none=True)
field_info = user.get_annotated_field_value(PrimaryKey)
assert field_info.value == 1
get_type()
Return the concrete generic type parameter supplied to the instance, or None.
from typing import Generic, TypeVar
from pydantic_super_model import SuperModelMixin
GenericType = TypeVar("GenericType")
class UserWithType(SuperModelMixin, Generic[GenericType]):
id: GenericType
name: str
def __init__(self, id: GenericType, name: str) -> None:
self.id = id
self.name = name
assert UserWithType[int](id=1, name="Charlie").get_type() is int
validate_not_implemented_fields()
Reject fields annotated with FieldNotImplemented. Raises NotImplementedError if any such fields have values.
SuperModelPydanticMixin: called automatically on construction.
from typing import Annotated
from pydantic_super_model import FieldNotImplemented, SuperModelPydanticMixin
class Experimental(SuperModelPydanticMixin):
test_field: Annotated[int, FieldNotImplemented]
name: str
Experimental(test_field=1, name="x") # raises NotImplementedError
SuperModelMixin: call manually in __init__ or __post_init__.
from typing import Annotated
from pydantic_super_model import FieldNotImplemented, SuperModelMixin
class Experimental(SuperModelMixin):
test_field: Annotated[int, FieldNotImplemented]
name: str
def __init__(self, test_field: int, name: str) -> None:
self.test_field = test_field
self.name = name
self.validate_not_implemented_fields()
Experimental(test_field=1, name="x") # raises NotImplementedError
AnnotatedFieldInfo
A NamedTuple returned by get_annotated_fields and get_annotated_field_value:
| Field | Type | Description |
|---|---|---|
value |
Any |
The field's current value |
annotation |
object |
The full type annotation |
metadata |
tuple[object, ...] |
All metadata from Annotated |
matched_metadata |
tuple[object, ...] |
Only the metadata that matched the query |
Metadata Instance Matching
When you pass a class (not an instance) to get_annotated_fields, it matches metadata by isinstance:
from typing import Annotated
from pydantic_super_model import SuperModelPydanticMixin
class ThemeColorOptions:
def __init__(self, *, palette: str, allow_gradients: bool) -> None:
self.palette = palette
self.allow_gradients = allow_gradients
ThemeColorField = Annotated[
str,
"theme_color",
ThemeColorOptions(palette="northern-lights", allow_gradients=True),
]
class ThemeConfig(SuperModelPydanticMixin):
accent_color: ThemeColorField
theme = ThemeConfig(accent_color="#7dd3fc")
field_info = theme.get_annotated_fields(ThemeColorOptions)["accent_color"]
assert isinstance(field_info.matched_metadata[0], ThemeColorOptions)
assert field_info.matched_metadata[0].palette == "northern-lights"
assert field_info.matched_metadata[0].allow_gradients is True
Development
Install dev dependencies:
pip install "pydantic-super-model[dev]"
Run the test suite:
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantic_super_model-2.0.0.tar.gz.
File metadata
- Download URL: pydantic_super_model-2.0.0.tar.gz
- Upload date:
- Size: 5.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bb026160c279c9de1f2e4c36fd677b4f63c6835bce64591a924d96ba3623e60
|
|
| MD5 |
fbd0d189b96e226faa07497eed8c1d42
|
|
| BLAKE2b-256 |
34a1dac2e298f7ac2cd1afad30cdb13b72479da5e79f0c8fe6dd546069ef0e35
|
Provenance
The following attestation bundles were made for pydantic_super_model-2.0.0.tar.gz:
Publisher:
publish-to-pypi.yml on julien777z/pydantic-super-model
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_super_model-2.0.0.tar.gz -
Subject digest:
3bb026160c279c9de1f2e4c36fd677b4f63c6835bce64591a924d96ba3623e60 - Sigstore transparency entry: 1281781322
- Sigstore integration time:
-
Permalink:
julien777z/pydantic-super-model@8b85a0211ea25bf79df3c923f7aac524a9fb5cf4 -
Branch / Tag:
refs/heads/claude/refactor-supermodel-mixin-N9Rjo - Owner: https://github.com/julien777z
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8b85a0211ea25bf79df3c923f7aac524a9fb5cf4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file pydantic_super_model-2.0.0-py3-none-any.whl.
File metadata
- Download URL: pydantic_super_model-2.0.0-py3-none-any.whl
- Upload date:
- Size: 7.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2284d598d061b7ed473b96704575d749f3b23dfbb4bd78e074af589c6fb67a84
|
|
| MD5 |
ea39500baecf22a84c7f138c7f1bed00
|
|
| BLAKE2b-256 |
2242e8613e7fc39ab93e515a42fdff31932e067ed52f1b726f3fe8f7185701e8
|
Provenance
The following attestation bundles were made for pydantic_super_model-2.0.0-py3-none-any.whl:
Publisher:
publish-to-pypi.yml on julien777z/pydantic-super-model
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantic_super_model-2.0.0-py3-none-any.whl -
Subject digest:
2284d598d061b7ed473b96704575d749f3b23dfbb4bd78e074af589c6fb67a84 - Sigstore transparency entry: 1281781346
- Sigstore integration time:
-
Permalink:
julien777z/pydantic-super-model@8b85a0211ea25bf79df3c923f7aac524a9fb5cf4 -
Branch / Tag:
refs/heads/claude/refactor-supermodel-mixin-N9Rjo - Owner: https://github.com/julien777z
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-to-pypi.yml@8b85a0211ea25bf79df3c923f7aac524a9fb5cf4 -
Trigger Event:
workflow_dispatch
-
Statement type: