Last active
October 13, 2023 19:22
-
-
Save laurent-laporte-pro/af348ae1eb8414935373a659e236a013 to your computer and use it in GitHub Desktop.
Metaclass that makes all fields of a Pydantic model optional.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from typing import Any, Dict, Optional, Tuple, Type | |
import pydantic.main | |
class AllOptionalMetaclass(pydantic.main.ModelMetaclass): | |
""" | |
Metaclass that makes all fields of a Pydantic model optional. | |
This metaclass modifies the class's annotations to make all fields | |
optional by wrapping them with the `Optional` type. | |
Usage: | |
class MyModel(BaseModel, metaclass=AllOptionalMetaclass): | |
field1: str | |
field2: int | |
... | |
The fields defined in the model will be automatically converted to optional | |
fields, allowing instances of the model to be created even if not all fields | |
are provided during initialization. | |
""" | |
def __new__( | |
cls: Type["AllOptionalMetaclass"], | |
name: str, | |
bases: Tuple[Type[Any], ...], | |
namespaces: Dict[str, Any], | |
**kwargs: Dict[str, Any], | |
) -> Any: | |
annotations = namespaces.get("__annotations__", {}) | |
for base in bases: | |
for ancestor in base.__mro__: | |
annotations.update(getattr(ancestor, "__annotations__", {})) | |
for field, field_type in annotations.items(): | |
if not field.startswith("__"): | |
# Optional fields are correctly handled | |
annotations[field] = Optional[annotations[field]] | |
namespaces["__annotations__"] = annotations | |
return super().__new__(cls, name, bases, namespaces) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment