From e30649dd13fe40327013dbe8382691ed76210f21 Mon Sep 17 00:00:00 2001 From: Savva Surenkov Date: Mon, 6 Nov 2023 23:46:29 +0400 Subject: [PATCH] Adapt v2 impl to latest mypy --- django_pydantic_field/compat/django.py | 2 +- django_pydantic_field/fields.pyi | 22 +++------------------- django_pydantic_field/v2/fields.py | 15 +++++++-------- django_pydantic_field/v2/types.py | 7 +++++-- pyproject.toml | 5 +++-- 5 files changed, 19 insertions(+), 32 deletions(-) diff --git a/django_pydantic_field/compat/django.py b/django_pydantic_field/compat/django.py index 682f126..57b99f3 100644 --- a/django_pydantic_field/compat/django.py +++ b/django_pydantic_field/compat/django.py @@ -21,7 +21,7 @@ try: from typing import get_args, get_origin except ImportError: - from typing_extensions import get_args, get_origin + from typing_extensions import get_args, get_origin # type: ignore[no-redef] from django.db.migrations.serializer import BaseSerializer, serializer_factory from django.db.migrations.writer import MigrationWriter diff --git a/django_pydantic_field/fields.pyi b/django_pydantic_field/fields.pyi index 728e2be..f0230b0 100644 --- a/django_pydantic_field/fields.pyi +++ b/django_pydantic_field/fields.pyi @@ -2,11 +2,9 @@ from __future__ import annotations import typing as ty -from pydantic import BaseModel +from pydantic import BaseModel, ConfigDict from pydantic.dataclasses import DataclassClassOrWrapper -from .compat.pydantic import PYDANTIC_V1, PYDANTIC_V2 - __all__ = ("SchemaField",) SchemaT: ty.TypeAlias = ty.Union[ @@ -20,22 +18,11 @@ SchemaT: ty.TypeAlias = ty.Union[ OptSchemaT: ty.TypeAlias = ty.Optional[SchemaT] ST = ty.TypeVar("ST", bound=SchemaT) -ConfigType: ty.TypeAlias = ty.Any - -if PYDANTIC_V1: - from pydantic import ConfigDict, BaseConfig - - ConfigType = ty.Union[ConfigDict, type[BaseConfig], type] -elif PYDANTIC_V2: - from pydantic import ConfigDict - - ConfigType = ConfigDict - @ty.overload def SchemaField( schema: type[ST | None] | ty.ForwardRef = ..., - config: ConfigType = ..., + config: ConfigDict = ..., default: OptSchemaT | ty.Callable[[], OptSchemaT] = ..., *args, null: ty.Literal[True], @@ -47,13 +34,10 @@ def SchemaField( @ty.overload def SchemaField( schema: type[ST] | ty.ForwardRef = ..., - config: ConfigType = ..., + config: ConfigDict = ..., default: ty.Union[SchemaT, ty.Callable[[], SchemaT]] = ..., *args, null: ty.Literal[False] = ..., **kwargs, ) -> ST: ... - -def SchemaField(*args, **kwargs) -> ty.Any: - ... diff --git a/django_pydantic_field/v2/fields.py b/django_pydantic_field/v2/fields.py index 652d58f..3b59439 100644 --- a/django_pydantic_field/v2/fields.py +++ b/django_pydantic_field/v2/fields.py @@ -27,7 +27,8 @@ def __set__(self, obj, value): class PydanticSchemaField(JSONField, ty.Generic[types.ST]): - descriptor_class = SchemaAttribute + descriptor_class: type[DeferredAttribute] = SchemaAttribute + adapter: types.SchemaAdapter def __init__( self, @@ -76,23 +77,21 @@ def to_python(self, value: ty.Any): try: return self.adapter.validate_python(value) except pydantic.ValidationError as exc: - raise exceptions.ValidationError(exc.title, code="invalid", params=exc.errors()) from exc + error_params = {"errors": exc.errors(), "field": self} + raise exceptions.ValidationError(exc.title, code="invalid", params=error_params) from exc def get_prep_value(self, value: ty.Any): if isinstance(value, BaseExpression): # We don't want to perform coercion on database query expressions. return super().get_prep_value(value) - try: - prep_value = self.adapter.validate_python(value, strict=True) - except pydantic.ValidationError: - prep_value = self.adapter.dump_python(value) - prep_value = self.adapter.validate_python(prep_value) - + prep_value = self.adapter.validate_python(value) plain_value = self.adapter.dump_python(prep_value) + return super().get_prep_value(plain_value) def get_transform(self, lookup_name: str): + transform: type[Transform] | SchemaKeyTransformAdapter | None transform = super().get_transform(lookup_name) if transform is not None: transform = SchemaKeyTransformAdapter(transform) diff --git a/django_pydantic_field/v2/types.py b/django_pydantic_field/v2/types.py index 1dfc9fe..9747ec7 100644 --- a/django_pydantic_field/v2/types.py +++ b/django_pydantic_field/v2/types.py @@ -29,6 +29,7 @@ class ExportKwargs(ty.TypedDict, total=False): strict: bool + from_attributes: bool mode: ty.Literal["json", "python"] include: IncEx | None exclude: IncEx | None @@ -80,11 +81,13 @@ def validate_schema(self) -> None: """Validate the schema and raise an exception if it is invalid.""" self._get_prepared_schema() - def validate_python(self, value: ty.Any, *, strict: bool | None = None) -> ST: + def validate_python(self, value: ty.Any, *, strict: bool | None = None, from_attributes: bool | None = None) -> ST: """Validate the value and raise an exception if it is invalid.""" if strict is None: strict = self.export_kwargs.get("strict", None) - return self.type_adapter.validate_python(value, strict=strict) + if from_attributes is None: + from_attributes = self.export_kwargs.get("from_attributes", None) + return self.type_adapter.validate_python(value, strict=strict, from_attributes=from_attributes) def dump_python(self, value: ty.Any) -> ty.Any: """Dump the value to a Python object.""" diff --git a/pyproject.toml b/pyproject.toml index 1b095f6..11a4a1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -51,8 +51,8 @@ dev = [ "mypy", "pytest==7.0.*", "djangorestframework>=3.11,<4", - "django-stubs[compatible-mypy]~=1.12.0", - "djangorestframework-stubs[compatible-mypy]~=1.7.0", + "django-stubs[compatible-mypy]~=4.2", + "djangorestframework-stubs[compatible-mypy]~=3.14", "pytest-django>=4.5,<5", ] test = [ @@ -106,6 +106,7 @@ plugins = [ "mypy_drf_plugin.main" ] exclude = [".env", "tests"] +enable_incomplete_feature = ["Unpack"] [tool.django-stubs] django_settings_module = "tests.settings.django_test_settings"