Force pydantic v1 for sqlmodel compatibility (#3026)

This commit is contained in:
Masen Furer 2024-04-11 13:42:30 -07:00 committed by GitHub
parent 9073a2781b
commit d7abcd45de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 58 additions and 4 deletions

View File

@ -76,4 +76,10 @@ jobs:
export PYTHONUNBUFFERED=1 export PYTHONUNBUFFERED=1
export REDIS_URL=redis://localhost:6379 export REDIS_URL=redis://localhost:6379
poetry run pytest tests --cov --no-cov-on-fail --cov-report= poetry run pytest tests --cov --no-cov-on-fail --cov-report=
# Change to explicitly install v1 when reflex-hosting-cli is compatible with v2
- name: Run unit tests w/ pydantic v2
run: |
export PYTHONUNBUFFERED=1
poetry run pip install "pydantic>2"
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
- run: poetry run coverage html - run: poetry run coverage html

View File

@ -11,7 +11,7 @@ try:
# reflex-hosting-cli tools are compatible with pydantic v2 # reflex-hosting-cli tools are compatible with pydantic v2
if not TYPE_CHECKING: if not TYPE_CHECKING:
import pydantic.v1.fields as ModelField from pydantic.v1.fields import ModelField
else: else:
raise ModuleNotFoundError raise ModuleNotFoundError
except ModuleNotFoundError: except ModuleNotFoundError:

View File

@ -16,12 +16,12 @@ import alembic.script
import alembic.util import alembic.util
import sqlalchemy import sqlalchemy
import sqlalchemy.orm import sqlalchemy.orm
import sqlmodel
from reflex import constants from reflex import constants
from reflex.base import Base from reflex.base import Base
from reflex.config import get_config from reflex.config import get_config
from reflex.utils import console from reflex.utils import console
from reflex.utils.compat import sqlmodel
def get_engine(url: str | None = None): def get_engine(url: str | None = None):

43
reflex/utils/compat.py Normal file
View File

@ -0,0 +1,43 @@
"""Compatibility hacks and helpers."""
import contextlib
import sys
@contextlib.contextmanager
def pydantic_v1_patch():
"""A context manager that patches the Pydantic module to mimic v1 behaviour.
Yields:
None when the Pydantic module is patched.
"""
patched_modules = [
"pydantic",
"pydantic.fields",
"pydantic.errors",
"pydantic.main",
]
originals = {module: sys.modules.get(module) for module in patched_modules}
try:
import pydantic.v1 # type: ignore
sys.modules["pydantic.fields"] = pydantic.v1.fields # type: ignore
sys.modules["pydantic.main"] = pydantic.v1.main # type: ignore
sys.modules["pydantic.errors"] = pydantic.v1.errors # type: ignore
sys.modules["pydantic"] = pydantic.v1
yield
except (ImportError, AttributeError):
# pydantic v1 is already installed
yield
finally:
# Restore the original Pydantic module
for k, original in originals.items():
if k in sys.modules:
if original:
sys.modules[k] = original
else:
del sys.modules[k]
with pydantic_v1_patch():
import sqlmodel as sqlmodel

View File

@ -29,7 +29,7 @@ try:
# reflex-hosting-cli tools are compatible with pydantic v2 # reflex-hosting-cli tools are compatible with pydantic v2
if not TYPE_CHECKING: if not TYPE_CHECKING:
import pydantic.v1.fields as ModelField from pydantic.v1.fields import ModelField
else: else:
raise ModuleNotFoundError raise ModuleNotFoundError
except ModuleNotFoundError: except ModuleNotFoundError:

View File

@ -1,12 +1,17 @@
from typing import Dict, List, Set, Tuple from typing import Dict, List, Set, Tuple
import pytest import pytest
from pydantic import ValidationError
from reflex.components import box, foreach, text, theme from reflex.components import box, foreach, text, theme
from reflex.components.core import Foreach from reflex.components.core import Foreach
from reflex.state import BaseState from reflex.state import BaseState
try:
# When pydantic v2 is installed
from pydantic.v1 import ValidationError # type: ignore
except ImportError:
from pydantic import ValidationError
class ForEachState(BaseState): class ForEachState(BaseState):
"""A state for testing the ForEach component.""" """A state for testing the ForEach component."""