Skip to content

Pydantic Contracts

This module contains JsonPatchFor, the factory used to bind patch documents to a target schema and an operation registry.

JsonPatchFor

Bases: _RegistryBoundPatchRoot, Generic[TargetT, RegistryT]

Factory for creating typed JSON Patch models bound to a registry declaration.

JsonPatchFor[Target, Registry] produces a patch model. Target is either a Pydantic model or Literal["SchemaName"] for JSON documents. Registry is a union of concrete OperationSchemas (OpA | OpB | ...).

Source code in jsonpatchx/pydantic.py
class JsonPatchFor(_RegistryBoundPatchRoot, Generic[TargetT, RegistryT]):
    """
    Factory for creating typed JSON Patch models bound to a registry declaration.

    ``JsonPatchFor[Target, Registry]`` produces a patch model.
    ``Target`` is either a Pydantic model or ``Literal["SchemaName"]`` for JSON documents.
    ``Registry`` is a union of concrete OperationSchemas (``OpA | OpB | ...``).
    """

    if TYPE_CHECKING:
        # At runtime, JsonPatchFor[X] returns either a _BasePatchBody or a BasePatchModel, each with their own apply().
        # Tell type checkers that JsonPatchFor[X] has an apply() to expose this.

        @overload
        def apply[TargetModelM: BaseModel](
            self: JsonPatchFor[TargetModelM, RegistryT], target: TargetModelM
        ) -> TargetModelM:
            """
            Apply this patch to ``target`` and return the patched Model.

            Args:
                target: The target BaseModel.

            Returns:
                patched: The patched BaseModel.

            Raises:
                PatchValidationError: Patched data fails validation for the target model.
                PatchError: Any patch-domain error raised by operations, including conflicts.
                    ``PatchInternalError`` is a ``PatchError`` raised for unexpected failures.
            """
            pass

        @overload
        def apply[TargetNameN: str](
            self: JsonPatchFor[TargetNameN, RegistryT],
            doc: JSONValue,
            *,
            inplace: bool = False,
        ) -> JSONValue:
            """
            Apply this patch to ``doc`` and return the patched document.

            Args:
                doc: The target JSON document.
                inplace: Copy policy. ``False`` deep-copies ``doc`` first; ``True``
                    applies operations against ``doc`` without that copy. This does
                    not guarantee returned root object identity.

            Return:
                patched: The patched JSON document.

            Raises:
                ValidationError: If the input is not a mutable ``JSONValue``.
                PatchError: Any patch-domain error raised by operations, including conflicts.
                    ``PatchInternalError`` is a ``PatchError`` raised for unexpected failures.
            """
            pass

        def apply(self, *args: Any, **kwargs: Any) -> Any:
            """
            Apply a JSON Patch document.

            Raises:
                TypeError: Model variant expects a Pydantic BaseModel instance.
                ValidationError: If the input is not a mutable ``JSONValue``.
                PatchValidationError: Patched data fails validation for the target model.
                PatchError: Any patch-domain error raised by operations, including conflicts.
                    ``PatchInternalError`` is a ``PatchError`` raised for unexpected failures.
            """
            pass

    @override
    def __class_getitem__(cls, params: object) -> type[_RegistryBoundPatchRoot]:
        if not isinstance(params, tuple) or len(params) != 2:
            raise TypeError(
                "JsonPatchFor expects JsonPatchFor[Target, Registry] where "
                'Target is a BaseModel subclass or Literal["SchemaName"] and '
                "Registry is a union of concrete OperationSchemas. "
                f"Got: {params!r}."
            )

        target, registry = params
        registry = _RegistrySpec.from_typeform(registry)

        schema_name = _coerce_schema_name(target)
        if schema_name is not None:
            return cls._create_json_patch_body(schema_name, registry)

        if not isclass(target) or not issubclass(target, BaseModel):
            raise TypeError(
                'JsonPatchFor[...] expects a Pydantic BaseModel subclass or Literal["SchemaName"], '
                f"got {target!r}"
            )

        return cls._create_model_patch_body(target, registry)

    @staticmethod
    def _create_json_patch_body(
        schema_name: str,
        registry: _RegistrySpec,
    ) -> type[_BasePatchBody]:
        BodyPatchOperation = TypeAliasType(  # type: ignore[misc]
            f"{schema_name}PatchOperation",
            Annotated[
                registry.union_type,
                Field(
                    title=f"{schema_name} Patch Operation",
                    description=(
                        f"Discriminated union of patch operations for {schema_name}."
                    ),
                ),
            ],
        )  # NOTE: can't use type keyword because otherwise OpenAPI title binds to "BodyPatchOperation" instead

        PatchBody = create_model(
            f"{schema_name}PatchRequest",
            __base__=_BasePatchBody,
            __config__=ConfigDict(
                title=f"{schema_name} Patch Request",
                json_schema_extra={
                    "description": f"Array of patch operations for {schema_name}.",
                },
            ),
            root=(list[BodyPatchOperation], ...),  # type: ignore[valid-type]
        )

        PatchBody.__registry__ = registry
        PatchBody.__doc__ = (
            f"Discriminated union of patch operations for {schema_name}."
        )
        return PatchBody

    @staticmethod
    def _create_model_patch_body(
        model: type[ModelT],
        registry: _RegistrySpec,
    ) -> type[_BasePatchModel[ModelT]]:
        ModelPatchOperation = TypeAliasType(  # type: ignore[misc]
            f"{model.__name__}PatchOperation",
            Annotated[
                registry.union_type,
                Field(
                    title=f"{model.__name__} Patch Operation",
                    description=f"Discriminated union of patch operations for {model.__name__}.",
                ),
            ],
        )  # NOTE: can't use type keyword because otherwise OpenAPI title binds to "ModelPatchOperation" instead

        PatchModel = create_model(
            f"{model.__name__}PatchRequest",
            __base__=_BasePatchModel,
            __config__=ConfigDict(
                title=f"{model.__name__} Patch Request",
                json_schema_extra={
                    "description": (
                        f"Array of patch operations for {model.__name__}. "
                        "Applied to model_dump() and re-validated against the model schema."
                    ),
                    "x-target-model": model.__name__,
                },
            ),
            root=(list[ModelPatchOperation], ...),  # type: ignore[valid-type]
        )

        PatchModel.__target_model__ = model
        PatchModel.__registry__ = registry
        PatchModel.__doc__ = f"Array of patch operations for {model.__name__}."
        return PatchModel

apply(*args, **kwargs)

apply(target: TargetModelM) -> TargetModelM
apply(
    doc: JSONValue, *, inplace: bool = False
) -> JSONValue

Apply a JSON Patch document.

Raises:

Type Description
TypeError

Model variant expects a Pydantic BaseModel instance.

ValidationError

If the input is not a mutable JSONValue.

PatchValidationError

Patched data fails validation for the target model.

PatchError

Any patch-domain error raised by operations, including conflicts. PatchInternalError is a PatchError raised for unexpected failures.

Source code in jsonpatchx/pydantic.py
def apply(self, *args: Any, **kwargs: Any) -> Any:
    """
    Apply a JSON Patch document.

    Raises:
        TypeError: Model variant expects a Pydantic BaseModel instance.
        ValidationError: If the input is not a mutable ``JSONValue``.
        PatchValidationError: Patched data fails validation for the target model.
        PatchError: Any patch-domain error raised by operations, including conflicts.
            ``PatchInternalError`` is a ``PatchError`` raised for unexpected failures.
    """
    pass