Skip to content

Commit c387664

Browse files
committed
move pydantic, sqlmodel, alembic to optional dependencies
1 parent abf9d2e commit c387664

24 files changed

Lines changed: 798 additions & 801 deletions

pyi_hashes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"reflex/components/core/upload.pyi": "90039579d573a7aa887d40889ced6154",
2424
"reflex/components/datadisplay/__init__.pyi": "cf087efa8b3960decc6b231cc986cfa9",
2525
"reflex/components/datadisplay/code.pyi": "735a3bc0693e5c9bcb52af072aa10bd7",
26-
"reflex/components/datadisplay/dataeditor.pyi": "82ff9fcac9209b8edabe9798f3d3bd9f",
26+
"reflex/components/datadisplay/dataeditor.pyi": "cfdb99a776d9b3ed2ccb20d80d1b584a",
2727
"reflex/components/datadisplay/shiki_code_block.pyi": "ecbb5e9d66023204ae2c23cc3f6a640c",
2828
"reflex/components/el/__init__.pyi": "f07f5957ca4dc3d95ffdc2ddb75fe2f8",
2929
"reflex/components/el/element.pyi": "3519fdc9a74aab7b02f475568def5f95",
@@ -119,5 +119,5 @@
119119
"reflex/components/recharts/general.pyi": "e992c57e3df1dc47b6a544c535d87319",
120120
"reflex/components/recharts/polar.pyi": "cb74280e343562f52f087973eff27389",
121121
"reflex/components/recharts/recharts.pyi": "2ab851e1b2cb63ae690e89144319a02c",
122-
"reflex/components/sonner/toast.pyi": "1681a04ee927febbe7b015dc224ba00b"
122+
"reflex/components/sonner/toast.pyi": "ad14cd7173596a689b3c9f7bf55f5ff7"
123123
}

pyproject.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,18 @@ readme = "README.md"
2020
keywords = ["web", "framework"]
2121
requires-python = ">=3.10,<4.0"
2222
dependencies = [
23-
"alembic >=1.15.2,<2.0",
2423
"fastapi >=0.115.0",
2524
"granian[reload] >=2.2.5",
2625
"httpx >=0.28.0,<1.0",
2726
"jinja2 >=3.1.2,<4.0",
2827
"packaging >=24.2,<26",
2928
"platformdirs >=4.3.7,<5.0",
3029
"psutil >=7.0.0,<8.0",
31-
"pydantic >=1.10.21,<3.0",
3230
"python-socketio >=5.12.0,<6.0",
3331
"python-multipart >=0.0.20,<1.0",
3432
"redis >=5.2.1,<7.0",
3533
"reflex-hosting-cli >=0.1.47",
3634
"rich >=13,<15",
37-
"sqlmodel >=0.0.24,<0.1",
3835
"click >=8",
3936
"typing_extensions >=4.13.0",
4037
"wrapt >=1.17.0,<2.0",
@@ -49,6 +46,13 @@ classifiers = [
4946
"Programming Language :: Python :: 3.13",
5047
]
5148

49+
[project.optional-dependencies]
50+
db = [
51+
"alembic >=1.15.2,<2.0",
52+
"pydantic >=1.10.21,<3.0",
53+
"sqlmodel >=0.0.24,<0.1",
54+
]
55+
5256

5357
[project.urls]
5458
homepage = "https://reflex.dev"

reflex/app.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
IndividualEventType,
7979
get_hydrate_event,
8080
)
81-
from reflex.model import Model, get_db_status
8281
from reflex.page import DECORATED_PAGES
8382
from reflex.route import (
8483
get_route_args,
@@ -904,6 +903,8 @@ def _setup_admin_dash(self):
904903
try:
905904
from starlette_admin.contrib.sqla.admin import Admin
906905
from starlette_admin.contrib.sqla.view import ModelView
906+
907+
from reflex.model import Model
907908
except ImportError:
908909
return
909910

@@ -1763,6 +1764,8 @@ async def health(_request: Request) -> JSONResponse:
17631764
tasks = []
17641765

17651766
if prerequisites.check_db_used():
1767+
from reflex.model import get_db_status
1768+
17661769
tasks.append(get_db_status())
17671770
if prerequisites.check_redis_used():
17681771
tasks.append(prerequisites.get_redis_status())

reflex/base.py

Lines changed: 64 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,72 @@
11
"""Define the base Reflex class."""
22

3-
from __future__ import annotations
3+
from importlib.util import find_spec
44

5-
import os
6-
from typing import TYPE_CHECKING, Any
5+
if find_spec("pydantic") and find_spec("pydantic.v1"):
6+
from pydantic.v1 import BaseModel
77

8-
import pydantic.v1.main as pydantic_main
9-
from pydantic.v1 import BaseModel
10-
from pydantic.v1.fields import ModelField
8+
class Base(BaseModel):
9+
"""The base class subclassed by all Reflex classes.
1110
11+
This class wraps Pydantic and provides common methods such as
12+
serialization and setting fields.
1213
13-
def validate_field_name(bases: list[type[BaseModel]], field_name: str) -> None:
14-
"""Ensure that the field's name does not shadow an existing attribute of the model.
15-
16-
Args:
17-
bases: List of base models to check for shadowed attrs.
18-
field_name: name of attribute
19-
20-
Raises:
21-
VarNameError: If state var field shadows another in its parent state
22-
"""
23-
from reflex.utils.exceptions import VarNameError
24-
25-
# can't use reflex.config.environment here cause of circular import
26-
reload = os.getenv("__RELOAD_CONFIG", "").lower() == "true"
27-
base = None
28-
try:
29-
for base in bases:
30-
if not reload and getattr(base, field_name, None):
31-
pass
32-
except TypeError as te:
33-
msg = (
34-
f'State var "{field_name}" in {base} has been shadowed by a substate var; '
35-
f'use a different field name instead".'
36-
)
37-
raise VarNameError(msg) from te
38-
39-
40-
# monkeypatch pydantic validate_field_name method to skip validating
41-
# shadowed state vars when reloading app via utils.prerequisites.get_app(reload=True)
42-
pydantic_main.validate_field_name = validate_field_name # pyright: ignore [reportPrivateImportUsage]
43-
44-
if TYPE_CHECKING:
45-
from reflex.vars import Var
46-
47-
48-
class Base(BaseModel):
49-
"""The base class subclassed by all Reflex classes.
50-
51-
This class wraps Pydantic and provides common methods such as
52-
serialization and setting fields.
53-
54-
Any data structure that needs to be transferred between the
55-
frontend and backend should subclass this class.
56-
"""
57-
58-
class Config:
59-
"""Pydantic config."""
60-
61-
arbitrary_types_allowed = True
62-
use_enum_values = True
63-
extra = "allow"
64-
65-
def json(self) -> str:
66-
"""Convert the object to a json string.
67-
68-
Returns:
69-
The object as a json string.
14+
Any data structure that needs to be transferred between the
15+
frontend and backend should subclass this class.
7016
"""
71-
from reflex.utils.serializers import serialize
72-
73-
return self.__config__.json_dumps(
74-
self.dict(),
75-
default=serialize,
76-
)
77-
78-
def set(self, **kwargs: Any):
79-
"""Set multiple fields and return the object.
80-
81-
Args:
82-
**kwargs: The fields and values to set.
83-
84-
Returns:
85-
The object with the fields set.
86-
"""
87-
for key, value in kwargs.items():
88-
setattr(self, key, value)
89-
return self
90-
91-
@classmethod
92-
def get_fields(cls) -> dict[str, ModelField]:
93-
"""Get the fields of the object.
9417

95-
Returns:
96-
The fields of the object.
97-
"""
98-
return cls.__fields__
99-
100-
@classmethod
101-
def add_field(cls, var: Var, default_value: Any):
102-
"""Add a pydantic field after class definition.
103-
104-
Used by State.add_var() to correctly handle the new variable.
105-
106-
Args:
107-
var: The variable to add a pydantic field for.
108-
default_value: The default value of the field
109-
"""
110-
var_name = var._var_field_name
111-
new_field = ModelField.infer(
112-
name=var_name,
113-
value=default_value,
114-
annotation=var._var_type,
115-
class_validators=None,
116-
config=cls.__config__,
117-
)
118-
cls.__fields__.update({var_name: new_field})
119-
120-
def get_value(self, key: str) -> Any:
121-
"""Get the value of a field.
122-
123-
Args:
124-
key: The key of the field.
125-
126-
Returns:
127-
The value of the field.
128-
"""
129-
if isinstance(key, str):
130-
# Seems like this function signature was wrong all along?
131-
# If the user wants a field that we know of, get it and pass it off to _get_value
132-
return getattr(self, key)
133-
return key
18+
class Config:
19+
"""Pydantic config."""
20+
21+
arbitrary_types_allowed = True
22+
use_enum_values = True
23+
extra = "allow"
24+
25+
def __init__(self, *args, **kwargs):
26+
"""Initialize the base class.
27+
28+
Args:
29+
*args: Positional arguments.
30+
**kwargs: Keyword arguments.
31+
"""
32+
from reflex.utils import console
33+
34+
console.deprecate(
35+
"rx.Base",
36+
"The `rx.Base` class is deprecated. You can use `pydantic.BaseModel` directly instead or a dataclass if possible.",
37+
deprecation_version="0.8.0",
38+
removal_version="0.9.0",
39+
)
40+
super().__init__(*args, **kwargs)
41+
42+
def json(self) -> str:
43+
"""Convert the object to a json string.
44+
45+
Returns:
46+
The object as a json string.
47+
"""
48+
from reflex.utils.serializers import serialize
49+
50+
return self.__config__.json_dumps(
51+
self.dict(),
52+
default=serialize,
53+
)
54+
else:
55+
56+
class PydanticNotFoundFallback:
57+
"""Fallback base class for environments without Pydantic."""
58+
59+
def __init__(self, *args, **kwargs):
60+
"""Initialize the base class.
61+
62+
Args:
63+
*args: Positional arguments.
64+
**kwargs: Keyword arguments.
65+
66+
Raises:
67+
ImportError: As Pydantic is not installed.
68+
"""
69+
msg = "Pydantic is not installed. Please install it to use rx.Base."
70+
raise ImportError(msg)
71+
72+
Base = PydanticNotFoundFallback # pyright: ignore[reportAssignmentType]

reflex/components/datadisplay/dataeditor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
from __future__ import annotations
44

5+
import dataclasses
56
from collections.abc import Mapping, Sequence
67
from enum import Enum
78
from typing import Any, Literal, TypedDict
89

9-
from reflex.base import Base
1010
from reflex.components.component import Component, NoSSRComponent
1111
from reflex.components.literals import LiteralRowMarker
1212
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
@@ -50,7 +50,8 @@ class GridColumnIcons(Enum):
5050
VideoUri = "video_uri"
5151

5252

53-
class DataEditorTheme(Base):
53+
@dataclasses.dataclass
54+
class DataEditorTheme:
5455
"""The theme for the DataEditor component."""
5556

5657
accent_color: str | None = None

reflex/components/sonner/toast.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
from __future__ import annotations
44

5+
import dataclasses
56
from typing import Any, Literal
67

7-
from reflex.base import Base
88
from reflex.components.component import Component, ComponentNamespace
99
from reflex.components.lucide.icon import Icon
1010
from reflex.components.props import NoExtrasAllowedProps
@@ -35,7 +35,8 @@
3535
)
3636

3737

38-
class ToastAction(Base):
38+
@dataclasses.dataclass
39+
class ToastAction:
3940
"""A toast action that render a button in the toast."""
4041

4142
label: str

0 commit comments

Comments
 (0)