From 4f4309cac728875f349bc1374fe1c0178680f6df Mon Sep 17 00:00:00 2001 From: Manabu Niseki Date: Sat, 8 Jul 2023 11:33:11 +0900 Subject: [PATCH 1/3] feat: allow using Pydantic v2 --- aredis_om/_compat.py | 19 +++++++++++++++++++ aredis_om/model/encoders.py | 3 +-- aredis_om/model/model.py | 18 ++++++++++++------ pyproject.toml | 2 +- tests/_compat.py | 7 +++++++ tests/test_hash_model.py | 10 +++++----- tests/test_json_model.py | 2 +- tests/test_oss_redis_features.py | 2 +- tests/test_pydantic_integrations.py | 2 +- 9 files changed, 48 insertions(+), 17 deletions(-) create mode 100644 aredis_om/_compat.py create mode 100644 tests/_compat.py diff --git a/aredis_om/_compat.py b/aredis_om/_compat.py new file mode 100644 index 00000000..0246e4f8 --- /dev/null +++ b/aredis_om/_compat.py @@ -0,0 +1,19 @@ +from pydantic.version import VERSION as PYDANTIC_VERSION + + +PYDANTIC_V2 = PYDANTIC_VERSION.startswith("2.") + +if PYDANTIC_V2: + from pydantic.v1 import BaseModel, validator + from pydantic.v1.fields import FieldInfo, ModelField, Undefined, UndefinedType + from pydantic.v1.json import ENCODERS_BY_TYPE + from pydantic.v1.main import ModelMetaclass, validate_model + from pydantic.v1.typing import NoArgAnyCallable + from pydantic.v1.utils import Representation +else: + from pydantic import BaseModel, validator + from pydantic.fields import FieldInfo, ModelField, Undefined, UndefinedType + from pydantic.json import ENCODERS_BY_TYPE + from pydantic.main import ModelMetaclass, validate_model + from pydantic.typing import NoArgAnyCallable + from pydantic.utils import Representation diff --git a/aredis_om/model/encoders.py b/aredis_om/model/encoders.py index 4007640f..2f90e481 100644 --- a/aredis_om/model/encoders.py +++ b/aredis_om/model/encoders.py @@ -31,8 +31,7 @@ from types import GeneratorType from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union -from pydantic import BaseModel -from pydantic.json import ENCODERS_BY_TYPE +from .._compat import ENCODERS_BY_TYPE, BaseModel SetIntStr = Set[Union[int, str]] diff --git a/aredis_om/model/model.py b/aredis_om/model/model.py index 73bbf2a2..c0db0e67 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -25,18 +25,24 @@ ) from more_itertools import ichunked -from pydantic import BaseModel, validator -from pydantic.fields import FieldInfo as PydanticFieldInfo -from pydantic.fields import ModelField, Undefined, UndefinedType -from pydantic.main import ModelMetaclass, validate_model -from pydantic.typing import NoArgAnyCallable -from pydantic.utils import Representation from redis.commands.json.path import Path from redis.exceptions import ResponseError from typing_extensions import Protocol, get_args, get_origin from ulid import ULID from .. import redis +from .._compat import BaseModel +from .._compat import FieldInfo as PydanticFieldInfo +from .._compat import ( + ModelField, + ModelMetaclass, + NoArgAnyCallable, + Representation, + Undefined, + UndefinedType, + validate_model, + validator, +) from ..checks import has_redis_json, has_redisearch from ..connections import get_redis_connection from ..util import ASYNC_MODE diff --git a/pyproject.toml b/pyproject.toml index 9a33b1fd..71680d8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ include=[ [tool.poetry.dependencies] python = ">=3.7,<4.0" redis = ">=3.5.3,<5.0.0" -pydantic = "^1.10.2" +pydantic = ">=1.10.2,<2.1.0" click = "^8.0.1" pptree = "^3.1" types-redis = ">=3.5.9,<5.0.0" diff --git a/tests/_compat.py b/tests/_compat.py new file mode 100644 index 00000000..eafe6b9c --- /dev/null +++ b/tests/_compat.py @@ -0,0 +1,7 @@ +from aredis_om._compat import PYDANTIC_V2 + + +if PYDANTIC_V2: + from pydantic.v1 import EmailStr, ValidationError +else: + from pydantic.v1 import EmailStr, ValidationError diff --git a/tests/test_hash_model.py b/tests/test_hash_model.py index 122e2730..dbb5aef4 100644 --- a/tests/test_hash_model.py +++ b/tests/test_hash_model.py @@ -10,7 +10,6 @@ import pytest import pytest_asyncio -from pydantic import ValidationError from aredis_om import ( Field, @@ -24,6 +23,7 @@ # We need to run this check as sync code (during tests) even in async mode # because we call it in the top-level module scope. from redis_om import has_redisearch +from tests._compat import ValidationError from .conftest import py_test_mark_asyncio @@ -165,11 +165,11 @@ async def test_delete_non_exist(members, m): async def test_full_text_search_queries(members, m): member1, member2, member3 = members - actual = await (m.Member.find(m.Member.bio % "great").all()) + actual = await m.Member.find(m.Member.bio % "great").all() assert actual == [member1] - actual = await (m.Member.find(~(m.Member.bio % "anxious")).sort_by("age").all()) + actual = await m.Member.find(~(m.Member.bio % "anxious")).sort_by("age").all() assert actual == [member1, member3] @@ -245,10 +245,10 @@ async def test_tag_queries_punctuation(m): ) await member2.save() - result = await (m.Member.find(m.Member.first_name == "Andrew, the Michael").first()) + result = await m.Member.find(m.Member.first_name == "Andrew, the Michael").first() assert result == member1 - result = await (m.Member.find(m.Member.last_name == "St. Brookins-on-Pier").first()) + result = await m.Member.find(m.Member.last_name == "St. Brookins-on-Pier").first() assert result == member1 # Notice that when we index and query multiple values that use the internal diff --git a/tests/test_json_model.py b/tests/test_json_model.py index ee220bda..8fec6c0a 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -10,7 +10,6 @@ import pytest import pytest_asyncio -from pydantic import ValidationError from aredis_om import ( EmbeddedJsonModel, @@ -25,6 +24,7 @@ # We need to run this check as sync code (during tests) even in async mode # because we call it in the top-level module scope. from redis_om import has_redis_json +from tests._compat import ValidationError from .conftest import py_test_mark_asyncio diff --git a/tests/test_oss_redis_features.py b/tests/test_oss_redis_features.py index e14f9558..4d5b0913 100644 --- a/tests/test_oss_redis_features.py +++ b/tests/test_oss_redis_features.py @@ -6,9 +6,9 @@ import pytest import pytest_asyncio -from pydantic import ValidationError from aredis_om import HashModel, Migrator, NotFoundError, RedisModelError +from tests._compat import ValidationError from .conftest import py_test_mark_asyncio diff --git a/tests/test_pydantic_integrations.py b/tests/test_pydantic_integrations.py index 5ff735f3..12d41a9a 100644 --- a/tests/test_pydantic_integrations.py +++ b/tests/test_pydantic_integrations.py @@ -4,9 +4,9 @@ import pytest import pytest_asyncio -from pydantic import EmailStr, ValidationError from aredis_om import Field, HashModel, Migrator +from tests._compat import EmailStr, ValidationError today = datetime.date.today() From caea4148a8766abdb38bffc7f81396bde9a1e335 Mon Sep 17 00:00:00 2001 From: Manabu Niseki Date: Sat, 8 Jul 2023 11:33:29 +0900 Subject: [PATCH 2/3] ci: ignore _compat.py file --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 412b7380..0b8bd91c 100644 --- a/Makefile +++ b/Makefile @@ -54,7 +54,7 @@ lint: $(INSTALL_STAMP) dist $(POETRY) run isort --profile=black --lines-after-imports=2 ./tests/ $(NAME) $(SYNC_NAME) $(POETRY) run black ./tests/ $(NAME) $(POETRY) run flake8 --ignore=W503,E501,F401,E731 ./tests/ $(NAME) $(SYNC_NAME) - $(POETRY) run mypy ./tests/ $(NAME) $(SYNC_NAME) --ignore-missing-imports + $(POETRY) run mypy ./tests/ $(NAME) $(SYNC_NAME) --ignore-missing-imports --exclude _compat\.py$ $(POETRY) run bandit -r $(NAME) $(SYNC_NAME) -s B608 .PHONY: format From 0d29d8a6548b888c35cf7fe18acc66a02a6daef6 Mon Sep 17 00:00:00 2001 From: Manabu Niseki Date: Sat, 8 Jul 2023 11:41:42 +0900 Subject: [PATCH 3/3] fix: fix typo --- tests/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/_compat.py b/tests/_compat.py index eafe6b9c..c21b47d2 100644 --- a/tests/_compat.py +++ b/tests/_compat.py @@ -4,4 +4,4 @@ if PYDANTIC_V2: from pydantic.v1 import EmailStr, ValidationError else: - from pydantic.v1 import EmailStr, ValidationError + from pydantic import EmailStr, ValidationError