diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6103d3f..d67f57e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,7 +76,7 @@ jobs: strategy: matrix: os: [ ubuntu-latest ] - pyver: ["3.7", "3.8", "3.9", "3.10", "3.11", "pypy-3.8", "pypy-3.9" ] + pyver: [ "3.8", "3.9", "3.10", "3.11", "pypy-3.8", "pypy-3.9" ] redisstack: [ "latest" ] fail-fast: false services: diff --git a/.github/workflows/spellcheck.yml b/.github/workflows/spellcheck.yml index ff5b50e..e152841 100644 --- a/.github/workflows/spellcheck.yml +++ b/.github/workflows/spellcheck.yml @@ -8,7 +8,7 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Check Spelling - uses: rojopolis/spellcheck-github-actions@0.29.0 + uses: rojopolis/spellcheck-github-actions@0.33.1 with: config_path: .github/spellcheck-settings.yml task_name: Markdown diff --git a/Makefile b/Makefile index 11ae817..f478ead 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 --exclude migrate.py + $(POETRY) run mypy ./tests/ $(NAME) $(SYNC_NAME) --ignore-missing-imports --exclude migrate.py --exclude _compat\.py$ $(POETRY) run bandit -r $(NAME) $(SYNC_NAME) -s B608 .PHONY: format diff --git a/README.md b/README.md index fe643fc..cf520f9 100644 --- a/README.md +++ b/README.md @@ -386,8 +386,8 @@ Redis OM uses the [MIT license][license-url]. [version-svg]: https://img.shields.io/pypi/v/redis-om?style=flat-square [package-url]: https://pypi.org/project/redis-om/ -[ci-svg]: https://img.shields.io/github/workflow/status/redis/redis-om-python/CI?style=flat-square -[ci-url]: https://github.com/redis/redis-om-python/actions/workflows/CI.yml +[ci-svg]: https://github.com/redis/redis-om-python/actions/workflows/ci.yml/badge.svg +[ci-url]: https://github.com/redis/redis-om-python/actions/workflows/ci.yml [license-image]: https://img.shields.io/badge/license-mit-green.svg?style=flat-square [license-url]: LICENSE diff --git a/aredis_om/_compat.py b/aredis_om/_compat.py new file mode 100644 index 0000000..0246e4f --- /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 4007640..2f90e48 100644 --- a/aredis_om/model/encoders.py +++ b/aredis_om/model/encoders.py @@ -31,8 +31,7 @@ from pathlib import PurePath 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 0ccc5b0..a4c6b9e 100644 --- a/aredis_om/model/model.py +++ b/aredis_om/model/model.py @@ -25,18 +25,24 @@ from typing import ( ) 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/docs/getting_started.md b/docs/getting_started.md index c9ada5d..a2d6ff1 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -6,15 +6,15 @@ This tutorial will walk you through installing Redis OM, creating your first mod ## Prerequisites -Redis OM requires Python version 3.7 or above and a Redis instance to connect to. +Redis OM requires Python version 3.8 or above and a Redis instance to connect to. ## Python -Make sure you are running **Python version 3.7 or higher**: +Make sure you are running **Python version 3.8 or higher**: ``` python --version -Python 3.7.0 +Python 3.8.0 ``` If you don't have Python installed, you can download it from [Python.org](https://www.python.org/downloads/), use [pyenv](https://github.com/pyenv/pyenv), or install Python with your operating system's package manager. diff --git a/pyproject.toml b/pyproject.toml index 52bb4dd..a38626e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "redis-om" -version = "0.1.3" +version = "0.3.0" description = "Object mappings, and more, for Redis." authors = ["Redis OSS "] maintainers = ["Redis OSS "] @@ -17,8 +17,6 @@ classifiers = [ "Operating System :: OS Independent", "Topic :: Database", 'License :: OSI Approved :: BSD License', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', @@ -35,15 +33,16 @@ include=[ "Issue tracker" = "https://github.com/redis/redis-om-python/issues" [tool.poetry.dependencies] -python = ">=3.7,<4.0" +python = "^3.9" redis = ">=3.5.3,<5.0.0" -pydantic = "^1.10.2" +pydantic = "^2.1.0" click = "^8.0.1" types-redis = ">=3.5.9,<5.0.0" -python-ulid = "^1.0.3" +python-ulid = "^2.0.0" typing-extensions = "^4.4.0" hiredis = "^2.2.3" more-itertools = ">=8.14,<10.0" +setuptools = "^69.1.0" [tool.poetry.dev-dependencies] mypy = "^0.982" @@ -58,7 +57,7 @@ pytest-cov = "^4.0.0" pytest-xdist = "^3.1.0" unasync = "^0.5.0" pytest-asyncio = "^0.20.3" -email-validator = "^1.3.0" +email-validator = "^2.0.0" tox = "^3.26.0" tox-pyenv = "^1.1.0" diff --git a/tests/_compat.py b/tests/_compat.py new file mode 100644 index 0000000..c21b47d --- /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 import EmailStr, ValidationError diff --git a/tests/test_hash_model.py b/tests/test_hash_model.py index 8fe4a60..38ca18e 100644 --- a/tests/test_hash_model.py +++ b/tests/test_hash_model.py @@ -10,7 +10,6 @@ from unittest import mock import pytest import pytest_asyncio -from pydantic import ValidationError from aredis_om import ( Field, @@ -24,6 +23,7 @@ from aredis_om import ( # 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 diff --git a/tests/test_json_model.py b/tests/test_json_model.py index ee220bd..8fec6c0 100644 --- a/tests/test_json_model.py +++ b/tests/test_json_model.py @@ -10,7 +10,6 @@ from unittest import mock import pytest import pytest_asyncio -from pydantic import ValidationError from aredis_om import ( EmbeddedJsonModel, @@ -25,6 +24,7 @@ from aredis_om import ( # 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 e14f955..4d5b091 100644 --- a/tests/test_oss_redis_features.py +++ b/tests/test_oss_redis_features.py @@ -6,9 +6,9 @@ from typing import Optional 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 5ff735f..12d41a9 100644 --- a/tests/test_pydantic_integrations.py +++ b/tests/test_pydantic_integrations.py @@ -4,9 +4,9 @@ from collections import namedtuple 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()