reflex 0.8.14a2__py3-none-any.whl → 0.8.15a0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflex might be problematic. Click here for more details.
- reflex/__init__.py +1 -1
- reflex/__init__.pyi +2 -1
- reflex/app.py +5 -2
- reflex/base.py +61 -33
- reflex/components/datadisplay/dataeditor.py +17 -2
- reflex/components/datadisplay/dataeditor.pyi +6 -2
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +2 -1
- reflex/components/sonner/toast.py +3 -2
- reflex/components/sonner/toast.pyi +3 -2
- reflex/constants/installer.py +3 -3
- reflex/environment.py +9 -1
- reflex/experimental/client_state.py +1 -1
- reflex/istate/proxy.py +35 -24
- reflex/model.py +530 -511
- reflex/plugins/tailwind_v4.py +2 -2
- reflex/reflex.py +16 -10
- reflex/state.py +21 -25
- reflex/testing.py +8 -6
- reflex/utils/build.py +11 -1
- reflex/utils/compat.py +11 -56
- reflex/utils/prerequisites.py +2 -1
- reflex/utils/serializers.py +21 -20
- reflex/utils/telemetry.py +0 -2
- reflex/utils/types.py +81 -90
- reflex/vars/base.py +2 -16
- reflex/vars/object.py +28 -1
- {reflex-0.8.14a2.dist-info → reflex-0.8.15a0.dist-info}/METADATA +5 -1
- {reflex-0.8.14a2.dist-info → reflex-0.8.15a0.dist-info}/RECORD +32 -32
- {reflex-0.8.14a2.dist-info → reflex-0.8.15a0.dist-info}/WHEEL +0 -0
- {reflex-0.8.14a2.dist-info → reflex-0.8.15a0.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.14a2.dist-info → reflex-0.8.15a0.dist-info}/licenses/LICENSE +0 -0
reflex/__init__.py
CHANGED
|
@@ -323,7 +323,7 @@ _MAPPING: dict = {
|
|
|
323
323
|
"SessionStorage",
|
|
324
324
|
],
|
|
325
325
|
"middleware": ["middleware", "Middleware"],
|
|
326
|
-
"model": ["asession", "session", "Model"],
|
|
326
|
+
"model": ["asession", "session", "Model", "ModelRegistry"],
|
|
327
327
|
"page": ["page"],
|
|
328
328
|
"state": [
|
|
329
329
|
"var",
|
reflex/__init__.pyi
CHANGED
|
@@ -151,7 +151,7 @@ from .experimental import _x
|
|
|
151
151
|
from .istate.storage import Cookie, LocalStorage, SessionStorage
|
|
152
152
|
from .istate.wrappers import get_state
|
|
153
153
|
from .middleware import Middleware, middleware
|
|
154
|
-
from .model import Model, asession, session
|
|
154
|
+
from .model import Model, ModelRegistry, asession, session
|
|
155
155
|
from .page import page
|
|
156
156
|
from .state import ComponentState, State, dynamic, var
|
|
157
157
|
from .style import Style, toggle_color_mode
|
|
@@ -192,6 +192,7 @@ __all__ = [
|
|
|
192
192
|
"LocalStorage",
|
|
193
193
|
"Middleware",
|
|
194
194
|
"Model",
|
|
195
|
+
"ModelRegistry",
|
|
195
196
|
"MomentDelta",
|
|
196
197
|
"NoSSRComponent",
|
|
197
198
|
"PropsBase",
|
reflex/app.py
CHANGED
|
@@ -83,7 +83,6 @@ from reflex.event import (
|
|
|
83
83
|
get_hydrate_event,
|
|
84
84
|
noop,
|
|
85
85
|
)
|
|
86
|
-
from reflex.model import Model, get_db_status
|
|
87
86
|
from reflex.page import DECORATED_PAGES
|
|
88
87
|
from reflex.route import (
|
|
89
88
|
get_route_args,
|
|
@@ -648,7 +647,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
648
647
|
|
|
649
648
|
for api_transformer in api_transformers:
|
|
650
649
|
if isinstance(api_transformer, Starlette):
|
|
651
|
-
# Mount the api to the
|
|
650
|
+
# Mount the api to the starlette app.
|
|
652
651
|
App._add_cors(api_transformer)
|
|
653
652
|
api_transformer.mount("", asgi_app)
|
|
654
653
|
asgi_app = api_transformer
|
|
@@ -957,6 +956,8 @@ class App(MiddlewareMixin, LifespanMixin):
|
|
|
957
956
|
try:
|
|
958
957
|
from starlette_admin.contrib.sqla.admin import Admin
|
|
959
958
|
from starlette_admin.contrib.sqla.view import ModelView
|
|
959
|
+
|
|
960
|
+
from reflex.model import Model
|
|
960
961
|
except ImportError:
|
|
961
962
|
return
|
|
962
963
|
|
|
@@ -1849,6 +1850,8 @@ async def health(_request: Request) -> JSONResponse:
|
|
|
1849
1850
|
tasks = []
|
|
1850
1851
|
|
|
1851
1852
|
if prerequisites.check_db_used():
|
|
1853
|
+
from reflex.model import get_db_status
|
|
1854
|
+
|
|
1852
1855
|
tasks.append(get_db_status())
|
|
1853
1856
|
if prerequisites.check_redis_used():
|
|
1854
1857
|
tasks.append(prerequisites.get_redis_status())
|
reflex/base.py
CHANGED
|
@@ -1,47 +1,75 @@
|
|
|
1
1
|
"""Define the base Reflex class."""
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from importlib.util import find_spec
|
|
4
4
|
|
|
5
|
+
if find_spec("pydantic") and find_spec("pydantic.v1"):
|
|
6
|
+
from pydantic.v1 import BaseModel
|
|
5
7
|
|
|
6
|
-
class Base(BaseModel):
|
|
7
|
-
|
|
8
|
+
class Base(BaseModel):
|
|
9
|
+
"""The base class subclassed by all Reflex classes.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
This class wraps Pydantic and provides common methods such as
|
|
12
|
+
serialization and setting fields.
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
Any data structure that needs to be transferred between the
|
|
15
|
+
frontend and backend should subclass this class.
|
|
16
|
+
"""
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
class Config:
|
|
19
|
+
"""Pydantic config."""
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
arbitrary_types_allowed = True
|
|
22
|
+
use_enum_values = True
|
|
23
|
+
extra = "allow"
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
def __init__(self, *args, **kwargs):
|
|
26
|
+
"""Initialize the base class.
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
Args:
|
|
29
|
+
*args: Positional arguments.
|
|
30
|
+
**kwargs: Keyword arguments.
|
|
31
|
+
"""
|
|
32
|
+
from reflex.utils import console
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
console.deprecate(
|
|
35
|
+
feature_name="rx.Base",
|
|
36
|
+
reason="You can subclass from `pydantic.BaseModel` directly instead or use dataclasses if possible.",
|
|
37
|
+
deprecation_version="0.8.2",
|
|
38
|
+
removal_version="0.9.0",
|
|
39
|
+
)
|
|
40
|
+
super().__init__(*args, **kwargs)
|
|
35
41
|
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
def json(self) -> str:
|
|
43
|
+
"""Convert the object to a json string.
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
Returns:
|
|
46
|
+
The object as a json string.
|
|
47
|
+
"""
|
|
48
|
+
from reflex.utils.serializers import serialize
|
|
41
49
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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 = (
|
|
70
|
+
"Pydantic is not installed. Please install it to use rx.Base."
|
|
71
|
+
"You can install it with `pip install pydantic`."
|
|
72
|
+
)
|
|
73
|
+
raise ImportError(msg)
|
|
74
|
+
|
|
75
|
+
Base = PydanticNotFoundFallback # pyright: ignore[reportAssignmentType]
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import dataclasses
|
|
5
6
|
from collections.abc import Mapping, Sequence
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from typing import Any, Literal, TypedDict
|
|
8
9
|
|
|
9
|
-
from reflex.base import Base
|
|
10
10
|
from reflex.components.component import Component, NoSSRComponent
|
|
11
11
|
from reflex.components.literals import LiteralRowMarker
|
|
12
12
|
from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
|
|
@@ -50,7 +50,8 @@ class GridColumnIcons(Enum):
|
|
|
50
50
|
VideoUri = "video_uri"
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
@dataclasses.dataclass
|
|
54
|
+
class DataEditorThemeBase:
|
|
54
55
|
"""The theme for the DataEditor component."""
|
|
55
56
|
|
|
56
57
|
accent_color: str | None = None
|
|
@@ -87,6 +88,20 @@ class DataEditorTheme(Base):
|
|
|
87
88
|
text_medium: str | None = None
|
|
88
89
|
|
|
89
90
|
|
|
91
|
+
@dataclasses.dataclass(init=False)
|
|
92
|
+
class DataEditorTheme(DataEditorThemeBase):
|
|
93
|
+
"""The theme for the DataEditor component."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, **kwargs: Any):
|
|
96
|
+
"""Initialize the DataEditorTheme.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
**kwargs: The keyword arguments to initialize the theme.
|
|
100
|
+
"""
|
|
101
|
+
kwargs = {format.to_snake_case(k): v for k, v in kwargs.items()}
|
|
102
|
+
super().__init__(**kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
90
105
|
class Bounds(TypedDict):
|
|
91
106
|
"""The bounds of the group header."""
|
|
92
107
|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
# ------------------- DO NOT EDIT ----------------------
|
|
4
4
|
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
5
5
|
# ------------------------------------------------------
|
|
6
|
+
import dataclasses
|
|
6
7
|
from collections.abc import Mapping, Sequence
|
|
7
8
|
from enum import Enum
|
|
8
9
|
from typing import Any, Literal, TypedDict
|
|
9
10
|
|
|
10
|
-
from reflex.base import Base
|
|
11
11
|
from reflex.components.component import NoSSRComponent
|
|
12
12
|
from reflex.components.core.breakpoints import Breakpoints
|
|
13
13
|
from reflex.event import EventType, PointerEventInfo
|
|
@@ -43,7 +43,8 @@ class GridColumnIcons(Enum):
|
|
|
43
43
|
Uri = "uri"
|
|
44
44
|
VideoUri = "video_uri"
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
@dataclasses.dataclass
|
|
47
|
+
class DataEditorThemeBase:
|
|
47
48
|
accent_color: str | None
|
|
48
49
|
accent_fg: str | None
|
|
49
50
|
accent_light: str | None
|
|
@@ -77,6 +78,9 @@ class DataEditorTheme(Base):
|
|
|
77
78
|
text_light: str | None
|
|
78
79
|
text_medium: str | None
|
|
79
80
|
|
|
81
|
+
@dataclasses.dataclass(init=False)
|
|
82
|
+
class DataEditorTheme(DataEditorThemeBase): ...
|
|
83
|
+
|
|
80
84
|
class Bounds(TypedDict):
|
|
81
85
|
x: int
|
|
82
86
|
y: int
|
reflex/components/lucide/icon.py
CHANGED
|
@@ -6,7 +6,7 @@ from reflex.utils.imports import ImportVar
|
|
|
6
6
|
from reflex.vars.base import LiteralVar, Var
|
|
7
7
|
from reflex.vars.sequence import LiteralStringVar, StringVar
|
|
8
8
|
|
|
9
|
-
LUCIDE_LIBRARY = "lucide-react@0.
|
|
9
|
+
LUCIDE_LIBRARY = "lucide-react@0.545.0"
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class LucideIconComponent(Component):
|
|
@@ -1129,6 +1129,7 @@ LUCIDE_ICON_LIST = [
|
|
|
1129
1129
|
"monitor",
|
|
1130
1130
|
"moon_star",
|
|
1131
1131
|
"moon",
|
|
1132
|
+
"motorbike",
|
|
1132
1133
|
"mountain_snow",
|
|
1133
1134
|
"mountain",
|
|
1134
1135
|
"mouse_off",
|
|
@@ -11,7 +11,7 @@ from reflex.components.core.breakpoints import Breakpoints
|
|
|
11
11
|
from reflex.event import EventType, PointerEventInfo
|
|
12
12
|
from reflex.vars.base import Var
|
|
13
13
|
|
|
14
|
-
LUCIDE_LIBRARY = "lucide-react@0.
|
|
14
|
+
LUCIDE_LIBRARY = "lucide-react@0.545.0"
|
|
15
15
|
|
|
16
16
|
class LucideIconComponent(Component):
|
|
17
17
|
@classmethod
|
|
@@ -1194,6 +1194,7 @@ LUCIDE_ICON_LIST = [
|
|
|
1194
1194
|
"monitor",
|
|
1195
1195
|
"moon_star",
|
|
1196
1196
|
"moon",
|
|
1197
|
+
"motorbike",
|
|
1197
1198
|
"mountain_snow",
|
|
1198
1199
|
"mountain",
|
|
1199
1200
|
"mouse_off",
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import dataclasses
|
|
5
6
|
from typing import Any, Literal
|
|
6
7
|
|
|
7
|
-
from reflex.base import Base
|
|
8
8
|
from reflex.components.component import Component, ComponentNamespace
|
|
9
9
|
from reflex.components.lucide.icon import Icon
|
|
10
10
|
from reflex.components.props import NoExtrasAllowedProps
|
|
@@ -35,7 +35,8 @@ toast_ref = Var(
|
|
|
35
35
|
)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
@dataclasses.dataclass
|
|
39
|
+
class ToastAction:
|
|
39
40
|
"""A toast action that render a button in the toast."""
|
|
40
41
|
|
|
41
42
|
label: str
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
# ------------------- DO NOT EDIT ----------------------
|
|
4
4
|
# This file was generated by `reflex/utils/pyi_generator.py`!
|
|
5
5
|
# ------------------------------------------------------
|
|
6
|
+
import dataclasses
|
|
6
7
|
from collections.abc import Mapping, Sequence
|
|
7
8
|
from typing import Any, Literal
|
|
8
9
|
|
|
9
|
-
from reflex.base import Base
|
|
10
10
|
from reflex.components.component import Component, ComponentNamespace
|
|
11
11
|
from reflex.components.core.breakpoints import Breakpoints
|
|
12
12
|
from reflex.components.lucide.icon import Icon
|
|
@@ -32,7 +32,8 @@ toast_ref = Var(
|
|
|
32
32
|
_var_data=VarData(imports={f"$/{Dirs.STATE_PATH}": [ImportVar(tag="refs")]}),
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
@dataclasses.dataclass
|
|
36
|
+
class ToastAction:
|
|
36
37
|
label: str
|
|
37
38
|
on_click: Any
|
|
38
39
|
|
reflex/constants/installer.py
CHANGED
|
@@ -87,7 +87,7 @@ def _determine_react_router_version() -> str:
|
|
|
87
87
|
|
|
88
88
|
|
|
89
89
|
def _determine_react_version() -> str:
|
|
90
|
-
default_version = "19.
|
|
90
|
+
default_version = "19.2.0"
|
|
91
91
|
if (version := os.getenv("REACT_VERSION")) and version != default_version:
|
|
92
92
|
from reflex.utils import console
|
|
93
93
|
|
|
@@ -143,11 +143,11 @@ class PackageJson(SimpleNamespace):
|
|
|
143
143
|
"postcss-import": "16.1.1",
|
|
144
144
|
"@react-router/dev": _react_router_version,
|
|
145
145
|
"@react-router/fs-routes": _react_router_version,
|
|
146
|
-
"vite": "npm:rolldown-vite@7.1.
|
|
146
|
+
"vite": "npm:rolldown-vite@7.1.14",
|
|
147
147
|
}
|
|
148
148
|
OVERRIDES = {
|
|
149
149
|
# This should always match the `react` version in DEPENDENCIES for recharts compatibility.
|
|
150
150
|
"react-is": _react_version,
|
|
151
151
|
"cookie": "1.0.2",
|
|
152
|
-
"vite": "npm:rolldown-vite@7.1.
|
|
152
|
+
"vite": "npm:rolldown-vite@7.1.14",
|
|
153
153
|
}
|
reflex/environment.py
CHANGED
|
@@ -24,6 +24,7 @@ from typing import (
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
from reflex import constants
|
|
27
|
+
from reflex.constants.base import LogLevel
|
|
27
28
|
from reflex.plugins import Plugin
|
|
28
29
|
from reflex.utils.exceptions import EnvironmentVarValueError
|
|
29
30
|
from reflex.utils.types import GenericType, is_union, value_inside_optional
|
|
@@ -204,7 +205,8 @@ def interpret_env_var_value(
|
|
|
204
205
|
The interpreted value.
|
|
205
206
|
|
|
206
207
|
Raises:
|
|
207
|
-
ValueError: If the
|
|
208
|
+
ValueError: If the value is invalid.
|
|
209
|
+
EnvironmentVarValueError: If the value is invalid for the specific type.
|
|
208
210
|
"""
|
|
209
211
|
field_type = value_inside_optional(field_type)
|
|
210
212
|
|
|
@@ -218,6 +220,12 @@ def interpret_env_var_value(
|
|
|
218
220
|
return interpret_boolean_env(value, field_name)
|
|
219
221
|
if field_type is str:
|
|
220
222
|
return value
|
|
223
|
+
if field_type is LogLevel:
|
|
224
|
+
loglevel = LogLevel.from_string(value)
|
|
225
|
+
if loglevel is None:
|
|
226
|
+
msg = f"Invalid log level value: {value} for {field_name}"
|
|
227
|
+
raise EnvironmentVarValueError(msg)
|
|
228
|
+
return loglevel
|
|
221
229
|
if field_type is int:
|
|
222
230
|
return interpret_int_env(value, field_name)
|
|
223
231
|
if field_type is Path:
|
|
@@ -236,7 +236,7 @@ class ClientStateVar(Var):
|
|
|
236
236
|
|
|
237
237
|
setter = ArgsFunctionOperationBuilder.create(
|
|
238
238
|
# remove patterns of ["*"] from the value_str using regex
|
|
239
|
-
args_names=(re.sub(r"
|
|
239
|
+
args_names=(re.sub(r"(\?\.)?\[\".*\"\]", "", value_str),)
|
|
240
240
|
if value_str.startswith("_")
|
|
241
241
|
else (),
|
|
242
242
|
return_expr=setter.call(value_var),
|
reflex/istate/proxy.py
CHANGED
|
@@ -9,19 +9,16 @@ import functools
|
|
|
9
9
|
import inspect
|
|
10
10
|
import json
|
|
11
11
|
from collections.abc import Callable, Sequence
|
|
12
|
+
from importlib.util import find_spec
|
|
12
13
|
from types import MethodType
|
|
13
14
|
from typing import TYPE_CHECKING, Any, SupportsIndex, TypeVar
|
|
14
15
|
|
|
15
|
-
import pydantic
|
|
16
16
|
import wrapt
|
|
17
|
-
from pydantic import BaseModel as BaseModelV2
|
|
18
|
-
from pydantic.v1 import BaseModel as BaseModelV1
|
|
19
|
-
from sqlalchemy.orm import DeclarativeBase
|
|
20
17
|
|
|
21
18
|
from reflex.base import Base
|
|
22
19
|
from reflex.utils import prerequisites
|
|
23
20
|
from reflex.utils.exceptions import ImmutableStateError
|
|
24
|
-
from reflex.utils.serializers import serializer
|
|
21
|
+
from reflex.utils.serializers import can_serialize, serialize, serializer
|
|
25
22
|
from reflex.vars.base import Var
|
|
26
23
|
|
|
27
24
|
if TYPE_CHECKING:
|
|
@@ -339,6 +336,34 @@ class ReadOnlyStateProxy(StateProxy):
|
|
|
339
336
|
raise NotImplementedError(msg)
|
|
340
337
|
|
|
341
338
|
|
|
339
|
+
if find_spec("pydantic"):
|
|
340
|
+
import pydantic
|
|
341
|
+
|
|
342
|
+
NEVER_WRAP_BASE_ATTRS = set(Base.__dict__) - {"set"} | set(
|
|
343
|
+
pydantic.BaseModel.__dict__
|
|
344
|
+
)
|
|
345
|
+
else:
|
|
346
|
+
NEVER_WRAP_BASE_ATTRS = {}
|
|
347
|
+
|
|
348
|
+
MUTABLE_TYPES = (
|
|
349
|
+
list,
|
|
350
|
+
dict,
|
|
351
|
+
set,
|
|
352
|
+
Base,
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
if find_spec("sqlalchemy"):
|
|
356
|
+
from sqlalchemy.orm import DeclarativeBase
|
|
357
|
+
|
|
358
|
+
MUTABLE_TYPES += (DeclarativeBase,)
|
|
359
|
+
|
|
360
|
+
if find_spec("pydantic"):
|
|
361
|
+
from pydantic import BaseModel as BaseModelV2
|
|
362
|
+
from pydantic.v1 import BaseModel as BaseModelV1
|
|
363
|
+
|
|
364
|
+
MUTABLE_TYPES += (BaseModelV1, BaseModelV2)
|
|
365
|
+
|
|
366
|
+
|
|
342
367
|
class MutableProxy(wrapt.ObjectProxy):
|
|
343
368
|
"""A proxy for a mutable object that tracks changes."""
|
|
344
369
|
|
|
@@ -371,11 +396,6 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
371
396
|
"setdefault",
|
|
372
397
|
}
|
|
373
398
|
|
|
374
|
-
# These internal attributes on rx.Base should NOT be wrapped in a MutableProxy.
|
|
375
|
-
__never_wrap_base_attrs__ = set(Base.__dict__) - {"set"} | set(
|
|
376
|
-
pydantic.BaseModel.__dict__
|
|
377
|
-
)
|
|
378
|
-
|
|
379
399
|
# Dynamically generated classes for tracking dataclass mutations.
|
|
380
400
|
__dataclass_proxies__: dict[type, type] = {}
|
|
381
401
|
|
|
@@ -539,7 +559,7 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
539
559
|
|
|
540
560
|
if (
|
|
541
561
|
isinstance(self.__wrapped__, Base)
|
|
542
|
-
and __name not in
|
|
562
|
+
and __name not in NEVER_WRAP_BASE_ATTRS
|
|
543
563
|
and hasattr(value, "__func__")
|
|
544
564
|
):
|
|
545
565
|
# Wrap methods called on Base subclasses, which might do _anything_
|
|
@@ -669,7 +689,10 @@ def serialize_mutable_proxy(mp: MutableProxy):
|
|
|
669
689
|
Returns:
|
|
670
690
|
The wrapped object.
|
|
671
691
|
"""
|
|
672
|
-
|
|
692
|
+
obj = mp.__wrapped__
|
|
693
|
+
if can_serialize(type(obj)):
|
|
694
|
+
return serialize(obj)
|
|
695
|
+
return obj
|
|
673
696
|
|
|
674
697
|
|
|
675
698
|
_orig_json_encoder_default = json.JSONEncoder.default
|
|
@@ -739,18 +762,6 @@ class ImmutableMutableProxy(MutableProxy):
|
|
|
739
762
|
)
|
|
740
763
|
|
|
741
764
|
|
|
742
|
-
# These types will be wrapped in MutableProxy
|
|
743
|
-
MUTABLE_TYPES = (
|
|
744
|
-
list,
|
|
745
|
-
dict,
|
|
746
|
-
set,
|
|
747
|
-
Base,
|
|
748
|
-
DeclarativeBase,
|
|
749
|
-
BaseModelV2,
|
|
750
|
-
BaseModelV1,
|
|
751
|
-
)
|
|
752
|
-
|
|
753
|
-
|
|
754
765
|
@functools.lru_cache(maxsize=1024)
|
|
755
766
|
def is_mutable_type(type_: type) -> bool:
|
|
756
767
|
"""Check if a type is mutable and should be wrapped.
|