From 2ae1a215fc5290559025a43acf7aa2b07a5c49a6 Mon Sep 17 00:00:00 2001 From: Tom Schraitle Date: Thu, 27 Jul 2023 20:05:58 +0200 Subject: [PATCH] Fix #422: Update advanced topic about Pydantic v2 --- docs/advanced/combine-pydantic-and-semver.rst | 60 +++++++++++++------ 1 file changed, 42 insertions(+), 18 deletions(-) diff --git a/docs/advanced/combine-pydantic-and-semver.rst b/docs/advanced/combine-pydantic-and-semver.rst index a00c2cff..1b68a59f 100644 --- a/docs/advanced/combine-pydantic-and-semver.rst +++ b/docs/advanced/combine-pydantic-and-semver.rst @@ -5,35 +5,59 @@ According to its homepage, `Pydantic `_ "enforces type hints at runtime, and provides user friendly errors when data is invalid." -To work with Pydantic, use the following steps: +To work with Pydantic>2.0, use the following steps: 1. Derive a new class from :class:`~semver.version.Version` - first and add the magic methods :py:meth:`__get_validators__` - and :py:meth:`__modify_schema__` like this: + first and add the magic methods :py:meth:`__get_pydantic_core_schema__` + and :py:meth:`__get_pydantic_json_schema__` like this: .. code-block:: python + from typing import Annotated, Any, Callable + from pydantic import GetJsonSchemaHandler + from pydantic_core import core_schema + from pydantic.json_schema import JsonSchemaValue from semver import Version - class PydanticVersion(Version): - @classmethod - def _parse(cls, version): - return cls.parse(version) + class _VersionPydanticAnnotation: @classmethod - def __get_validators__(cls): - """Return a list of validator methods for pydantic models.""" - yield cls._parse + def __get_pydantic_core_schema__( + cls, + _source_type: Any, + _handler: Callable[[Any], core_schema.CoreSchema], + ) -> core_schema.CoreSchema: + def validate_from_str(value: str) -> Version: + return Version.parse(value) + + from_str_schema = core_schema.chain_schema( + [ + core_schema.str_schema(), + core_schema.no_info_plain_validator_function(validate_from_str), + ] + ) + + return core_schema.json_or_python_schema( + json_schema=from_str_schema, + python_schema=core_schema.union_schema( + [ + core_schema.is_instance_schema(Version), + from_str_schema, + ] + ), + serialization=core_schema.plain_serializer_function_ser_schema( + lambda instance: instance.x + ), + ) @classmethod - def __modify_schema__(cls, field_schema): - """Inject/mutate the pydantic field schema in-place.""" - field_schema.update(examples=["1.0.2", - "2.15.3-alpha", - "21.3.15-beta+12345", - ] - ) + def __get_pydantic_json_schema__( + cls, _core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler + ) -> JsonSchemaValue: + return handler(core_schema.str_schema()) + + ManifestVersion = Annotated[Version, _VersionPydanticAnnotation] 2. Create a new model (in this example :class:`MyModel`) and derive it from :class:`pydantic.BaseModel`: @@ -43,7 +67,7 @@ To work with Pydantic, use the following steps: import pydantic class MyModel(pydantic.BaseModel): - version: PydanticVersion + version: _VersionPydanticAnnotation 3. Use your model like this: