Skip to content

Commit

Permalink
Force pydantic v1 for sqlmodel compatibility (#3026)
Browse files Browse the repository at this point in the history
  • Loading branch information
masenf committed Apr 11, 2024
1 parent 9073a27 commit d7abcd4
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/unit_tests.yml
Expand Up @@ -76,4 +76,10 @@ jobs:
export PYTHONUNBUFFERED=1
export REDIS_URL=redis://localhost:6379
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
2 changes: 1 addition & 1 deletion reflex/compiler/utils.py
Expand Up @@ -11,7 +11,7 @@
# reflex-hosting-cli tools are compatible with pydantic v2

if not TYPE_CHECKING:
import pydantic.v1.fields as ModelField
from pydantic.v1.fields import ModelField
else:
raise ModuleNotFoundError
except ModuleNotFoundError:
Expand Down
2 changes: 1 addition & 1 deletion reflex/model.py
Expand Up @@ -16,12 +16,12 @@
import alembic.util
import sqlalchemy
import sqlalchemy.orm
import sqlmodel

from reflex import constants
from reflex.base import Base
from reflex.config import get_config
from reflex.utils import console
from reflex.utils.compat import sqlmodel


def get_engine(url: str | None = None):
Expand Down
43 changes: 43 additions & 0 deletions reflex/utils/compat.py
@@ -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
2 changes: 1 addition & 1 deletion reflex/utils/types.py
Expand Up @@ -29,7 +29,7 @@
# reflex-hosting-cli tools are compatible with pydantic v2

if not TYPE_CHECKING:
import pydantic.v1.fields as ModelField
from pydantic.v1.fields import ModelField
else:
raise ModuleNotFoundError
except ModuleNotFoundError:
Expand Down
7 changes: 6 additions & 1 deletion tests/components/core/test_foreach.py
@@ -1,12 +1,17 @@
from typing import Dict, List, Set, Tuple

import pytest
from pydantic import ValidationError

from reflex.components import box, foreach, text, theme
from reflex.components.core import Foreach
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):
"""A state for testing the ForEach component."""
Expand Down

0 comments on commit d7abcd4

Please sign in to comment.