reflex 0.8.2a1__py3-none-any.whl → 0.8.3a2__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/.templates/web/utils/state.js +7 -2
- reflex/__init__.py +1 -0
- reflex/__init__.pyi +2 -0
- reflex/app.py +8 -11
- reflex/compiler/compiler.py +10 -38
- reflex/components/base/error_boundary.py +6 -5
- reflex/components/core/__init__.py +1 -0
- reflex/components/core/__init__.pyi +3 -0
- reflex/components/core/window_events.py +104 -0
- reflex/components/core/window_events.pyi +84 -0
- reflex/components/el/__init__.pyi +4 -0
- reflex/components/el/elements/__init__.py +1 -0
- reflex/components/el/elements/__init__.pyi +5 -0
- reflex/components/el/elements/forms.py +4 -2
- reflex/components/el/elements/typography.py +7 -0
- reflex/components/el/elements/typography.pyi +246 -0
- reflex/components/lucide/icon.py +303 -292
- reflex/components/lucide/icon.pyi +303 -292
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/config.py +3 -3
- reflex/constants/installer.py +2 -1
- reflex/environment.py +3 -0
- reflex/event.py +69 -8
- reflex/model.py +55 -0
- reflex/reflex.py +33 -0
- reflex/state.py +9 -4
- reflex/testing.py +180 -288
- reflex/utils/console.py +17 -0
- reflex/utils/exec.py +22 -5
- reflex/utils/processes.py +28 -38
- reflex/utils/types.py +1 -1
- {reflex-0.8.2a1.dist-info → reflex-0.8.3a2.dist-info}/METADATA +1 -1
- {reflex-0.8.2a1.dist-info → reflex-0.8.3a2.dist-info}/RECORD +37 -35
- {reflex-0.8.2a1.dist-info → reflex-0.8.3a2.dist-info}/WHEEL +0 -0
- {reflex-0.8.2a1.dist-info → reflex-0.8.3a2.dist-info}/entry_points.txt +0 -0
- {reflex-0.8.2a1.dist-info → reflex-0.8.3a2.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,7 +8,7 @@ from reflex.components.component import Component, MemoizationLeaf, NoSSRCompone
|
|
|
8
8
|
class Recharts(Component):
|
|
9
9
|
"""A component that wraps a recharts lib."""
|
|
10
10
|
|
|
11
|
-
library = "recharts@3.0
|
|
11
|
+
library = "recharts@3.1.0"
|
|
12
12
|
|
|
13
13
|
def _get_style(self) -> dict:
|
|
14
14
|
return {"wrapperStyle": self.style}
|
|
@@ -17,7 +17,7 @@ class Recharts(Component):
|
|
|
17
17
|
class RechartsCharts(NoSSRComponent, MemoizationLeaf):
|
|
18
18
|
"""A component that wraps a recharts lib."""
|
|
19
19
|
|
|
20
|
-
library = "recharts@3.0
|
|
20
|
+
library = "recharts@3.1.0"
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"]
|
reflex/config.py
CHANGED
|
@@ -294,14 +294,14 @@ class Config(BaseConfig):
|
|
|
294
294
|
if env_loglevel or self.loglevel != LogLevel.DEFAULT:
|
|
295
295
|
console.set_log_level(env_loglevel or self.loglevel)
|
|
296
296
|
|
|
297
|
-
# Add builtin plugins if not disabled.
|
|
298
|
-
self._add_builtin_plugins()
|
|
299
|
-
|
|
300
297
|
# Update the config from environment variables.
|
|
301
298
|
env_kwargs = self.update_from_env()
|
|
302
299
|
for key, env_value in env_kwargs.items():
|
|
303
300
|
setattr(self, key, env_value)
|
|
304
301
|
|
|
302
|
+
# Add builtin plugins if not disabled.
|
|
303
|
+
self._add_builtin_plugins()
|
|
304
|
+
|
|
305
305
|
# Update default URLs if ports were set
|
|
306
306
|
kwargs.update(env_kwargs)
|
|
307
307
|
self._non_default_attributes = set(kwargs.keys())
|
reflex/constants/installer.py
CHANGED
|
@@ -143,10 +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
|
-
"rolldown-vite": "7.0.
|
|
146
|
+
"rolldown-vite": "7.0.9",
|
|
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
|
+
"rollup": "4.44.2",
|
|
152
153
|
}
|
reflex/environment.py
CHANGED
|
@@ -650,6 +650,9 @@ class EnvironmentVariables:
|
|
|
650
650
|
# Enable full logging of debug messages to reflex user directory.
|
|
651
651
|
REFLEX_ENABLE_FULL_LOGGING: EnvVar[bool] = env_var(False)
|
|
652
652
|
|
|
653
|
+
# The path to the reflex errors log file. If not set, no separate error log will be used.
|
|
654
|
+
REFLEX_ERROR_LOG_FILE: EnvVar[Path | None] = env_var(None)
|
|
655
|
+
|
|
653
656
|
|
|
654
657
|
environment = EnvironmentVariables()
|
|
655
658
|
|
reflex/event.py
CHANGED
|
@@ -109,7 +109,7 @@ class EventActionsMixin:
|
|
|
109
109
|
"""
|
|
110
110
|
return dataclasses.replace(
|
|
111
111
|
self,
|
|
112
|
-
event_actions={"stopPropagation": True
|
|
112
|
+
event_actions={**self.event_actions, "stopPropagation": True},
|
|
113
113
|
)
|
|
114
114
|
|
|
115
115
|
@property
|
|
@@ -121,7 +121,7 @@ class EventActionsMixin:
|
|
|
121
121
|
"""
|
|
122
122
|
return dataclasses.replace(
|
|
123
123
|
self,
|
|
124
|
-
event_actions={"preventDefault": True
|
|
124
|
+
event_actions={**self.event_actions, "preventDefault": True},
|
|
125
125
|
)
|
|
126
126
|
|
|
127
127
|
def throttle(self, limit_ms: int) -> Self:
|
|
@@ -135,7 +135,7 @@ class EventActionsMixin:
|
|
|
135
135
|
"""
|
|
136
136
|
return dataclasses.replace(
|
|
137
137
|
self,
|
|
138
|
-
event_actions={"throttle": limit_ms
|
|
138
|
+
event_actions={**self.event_actions, "throttle": limit_ms},
|
|
139
139
|
)
|
|
140
140
|
|
|
141
141
|
def debounce(self, delay_ms: int) -> Self:
|
|
@@ -149,7 +149,7 @@ class EventActionsMixin:
|
|
|
149
149
|
"""
|
|
150
150
|
return dataclasses.replace(
|
|
151
151
|
self,
|
|
152
|
-
event_actions={"debounce": delay_ms
|
|
152
|
+
event_actions={**self.event_actions, "debounce": delay_ms},
|
|
153
153
|
)
|
|
154
154
|
|
|
155
155
|
@property
|
|
@@ -161,7 +161,7 @@ class EventActionsMixin:
|
|
|
161
161
|
"""
|
|
162
162
|
return dataclasses.replace(
|
|
163
163
|
self,
|
|
164
|
-
event_actions={"temporal": True
|
|
164
|
+
event_actions={**self.event_actions, "temporal": True},
|
|
165
165
|
)
|
|
166
166
|
|
|
167
167
|
|
|
@@ -1269,7 +1269,7 @@ def call_script(
|
|
|
1269
1269
|
Returns:
|
|
1270
1270
|
EventSpec: An event that will execute the client side javascript.
|
|
1271
1271
|
"""
|
|
1272
|
-
callback_kwargs = {}
|
|
1272
|
+
callback_kwargs = {"callback": None}
|
|
1273
1273
|
if callback is not None:
|
|
1274
1274
|
callback_kwargs = {
|
|
1275
1275
|
"callback": str(
|
|
@@ -2211,7 +2211,15 @@ class EventNamespace:
|
|
|
2211
2211
|
|
|
2212
2212
|
@overload
|
|
2213
2213
|
def __new__(
|
|
2214
|
-
cls,
|
|
2214
|
+
cls,
|
|
2215
|
+
func: None = None,
|
|
2216
|
+
*,
|
|
2217
|
+
background: bool | None = None,
|
|
2218
|
+
stop_propagation: bool | None = None,
|
|
2219
|
+
prevent_default: bool | None = None,
|
|
2220
|
+
throttle: int | None = None,
|
|
2221
|
+
debounce: int | None = None,
|
|
2222
|
+
temporal: bool | None = None,
|
|
2215
2223
|
) -> Callable[
|
|
2216
2224
|
[Callable[[BASE_STATE, Unpack[P]], Any]], EventCallback[Unpack[P]] # pyright: ignore [reportInvalidTypeVarUse]
|
|
2217
2225
|
]: ...
|
|
@@ -2222,6 +2230,11 @@ class EventNamespace:
|
|
|
2222
2230
|
func: Callable[[BASE_STATE, Unpack[P]], Any],
|
|
2223
2231
|
*,
|
|
2224
2232
|
background: bool | None = None,
|
|
2233
|
+
stop_propagation: bool | None = None,
|
|
2234
|
+
prevent_default: bool | None = None,
|
|
2235
|
+
throttle: int | None = None,
|
|
2236
|
+
debounce: int | None = None,
|
|
2237
|
+
temporal: bool | None = None,
|
|
2225
2238
|
) -> EventCallback[Unpack[P]]: ...
|
|
2226
2239
|
|
|
2227
2240
|
def __new__(
|
|
@@ -2229,6 +2242,11 @@ class EventNamespace:
|
|
|
2229
2242
|
func: Callable[[BASE_STATE, Unpack[P]], Any] | None = None,
|
|
2230
2243
|
*,
|
|
2231
2244
|
background: bool | None = None,
|
|
2245
|
+
stop_propagation: bool | None = None,
|
|
2246
|
+
prevent_default: bool | None = None,
|
|
2247
|
+
throttle: int | None = None,
|
|
2248
|
+
debounce: int | None = None,
|
|
2249
|
+
temporal: bool | None = None,
|
|
2232
2250
|
) -> (
|
|
2233
2251
|
EventCallback[Unpack[P]]
|
|
2234
2252
|
| Callable[[Callable[[BASE_STATE, Unpack[P]], Any]], EventCallback[Unpack[P]]]
|
|
@@ -2238,6 +2256,11 @@ class EventNamespace:
|
|
|
2238
2256
|
Args:
|
|
2239
2257
|
func: The function to wrap.
|
|
2240
2258
|
background: Whether the event should be run in the background. Defaults to False.
|
|
2259
|
+
stop_propagation: Whether to stop the event from bubbling up the DOM tree.
|
|
2260
|
+
prevent_default: Whether to prevent the default behavior of the event.
|
|
2261
|
+
throttle: Throttle the event handler to limit calls (in milliseconds).
|
|
2262
|
+
debounce: Debounce the event handler to delay calls (in milliseconds).
|
|
2263
|
+
temporal: Whether the event should be dropped when the backend is down.
|
|
2241
2264
|
|
|
2242
2265
|
Raises:
|
|
2243
2266
|
TypeError: If background is True and the function is not a coroutine or async generator. # noqa: DAR402
|
|
@@ -2246,6 +2269,30 @@ class EventNamespace:
|
|
|
2246
2269
|
The wrapped function.
|
|
2247
2270
|
"""
|
|
2248
2271
|
|
|
2272
|
+
def _build_event_actions():
|
|
2273
|
+
"""Build event_actions dict from decorator parameters.
|
|
2274
|
+
|
|
2275
|
+
Returns:
|
|
2276
|
+
Dict of event actions to apply, or empty dict if none specified.
|
|
2277
|
+
"""
|
|
2278
|
+
if not any(
|
|
2279
|
+
[stop_propagation, prevent_default, throttle, debounce, temporal]
|
|
2280
|
+
):
|
|
2281
|
+
return {}
|
|
2282
|
+
|
|
2283
|
+
event_actions = {}
|
|
2284
|
+
if stop_propagation is not None:
|
|
2285
|
+
event_actions["stopPropagation"] = stop_propagation
|
|
2286
|
+
if prevent_default is not None:
|
|
2287
|
+
event_actions["preventDefault"] = prevent_default
|
|
2288
|
+
if throttle is not None:
|
|
2289
|
+
event_actions["throttle"] = throttle
|
|
2290
|
+
if debounce is not None:
|
|
2291
|
+
event_actions["debounce"] = debounce
|
|
2292
|
+
if temporal is not None:
|
|
2293
|
+
event_actions["temporal"] = temporal
|
|
2294
|
+
return event_actions
|
|
2295
|
+
|
|
2249
2296
|
def wrapper(
|
|
2250
2297
|
func: Callable[[BASE_STATE, Unpack[P]], T],
|
|
2251
2298
|
) -> EventCallback[Unpack[P]]:
|
|
@@ -2281,8 +2328,22 @@ class EventNamespace:
|
|
|
2281
2328
|
object.__setattr__(func, "__name__", name)
|
|
2282
2329
|
object.__setattr__(func, "__qualname__", name)
|
|
2283
2330
|
state_cls._add_event_handler(name, func)
|
|
2284
|
-
|
|
2331
|
+
event_callback = getattr(state_cls, name)
|
|
2332
|
+
|
|
2333
|
+
# Apply decorator event actions
|
|
2334
|
+
event_actions = _build_event_actions()
|
|
2335
|
+
if event_actions:
|
|
2336
|
+
# Create new EventCallback with updated event_actions
|
|
2337
|
+
event_callback = dataclasses.replace(
|
|
2338
|
+
event_callback, event_actions=event_actions
|
|
2339
|
+
)
|
|
2340
|
+
|
|
2341
|
+
return event_callback
|
|
2285
2342
|
|
|
2343
|
+
# Store decorator event actions on the function for later processing
|
|
2344
|
+
event_actions = _build_event_actions()
|
|
2345
|
+
if event_actions:
|
|
2346
|
+
func._rx_event_actions = event_actions # pyright: ignore [reportFunctionMemberAccess]
|
|
2286
2347
|
return func # pyright: ignore [reportReturnType]
|
|
2287
2348
|
|
|
2288
2349
|
if func is not None:
|
reflex/model.py
CHANGED
|
@@ -19,6 +19,7 @@ import sqlalchemy.exc
|
|
|
19
19
|
import sqlalchemy.ext.asyncio
|
|
20
20
|
import sqlalchemy.orm
|
|
21
21
|
from alembic.runtime.migration import MigrationContext
|
|
22
|
+
from alembic.script.base import Script
|
|
22
23
|
|
|
23
24
|
from reflex.base import Base
|
|
24
25
|
from reflex.config import get_config
|
|
@@ -34,6 +35,41 @@ _AsyncSessionLocal: dict[str | None, sqlalchemy.ext.asyncio.async_sessionmaker]
|
|
|
34
35
|
from sqlmodel.ext.asyncio.session import AsyncSession # noqa: E402
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def format_revision(
|
|
39
|
+
rev: Script,
|
|
40
|
+
current_rev: str | None,
|
|
41
|
+
current_reached_ref: list[bool],
|
|
42
|
+
) -> str:
|
|
43
|
+
"""Format a single revision for display.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
rev: The alembic script object
|
|
47
|
+
current_rev: The currently applied revision ID
|
|
48
|
+
current_reached_ref: Mutable reference to track if we've reached current revision
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
Formatted string for display
|
|
52
|
+
"""
|
|
53
|
+
current = rev.revision
|
|
54
|
+
message = rev.doc
|
|
55
|
+
|
|
56
|
+
# Determine if this migration is applied
|
|
57
|
+
if current_rev is None:
|
|
58
|
+
is_applied = False
|
|
59
|
+
elif current == current_rev:
|
|
60
|
+
is_applied = True
|
|
61
|
+
current_reached_ref[0] = True
|
|
62
|
+
else:
|
|
63
|
+
is_applied = not current_reached_ref[0]
|
|
64
|
+
|
|
65
|
+
# Show checkmark or X with colors
|
|
66
|
+
status_icon = "[green]✓[/green]" if is_applied else "[red]✗[/red]"
|
|
67
|
+
head_marker = " (head)" if rev.is_head else ""
|
|
68
|
+
|
|
69
|
+
# Format output with message
|
|
70
|
+
return f" [{status_icon}] {current}{head_marker}, {message}"
|
|
71
|
+
|
|
72
|
+
|
|
37
73
|
def _safe_db_url_for_logging(url: str) -> str:
|
|
38
74
|
"""Remove username and password from the database URL for logging.
|
|
39
75
|
|
|
@@ -361,6 +397,25 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
|
|
|
361
397
|
directory=str(environment.ALEMBIC_CONFIG.get().parent / "alembic"),
|
|
362
398
|
)
|
|
363
399
|
|
|
400
|
+
@classmethod
|
|
401
|
+
def get_migration_history(cls):
|
|
402
|
+
"""Get migration history with current database state.
|
|
403
|
+
|
|
404
|
+
Returns:
|
|
405
|
+
tuple: (current_revision, revisions_list) where revisions_list is in chronological order
|
|
406
|
+
"""
|
|
407
|
+
# Get current revision from database
|
|
408
|
+
with cls.get_db_engine().connect() as connection:
|
|
409
|
+
context = MigrationContext.configure(connection)
|
|
410
|
+
current_rev = context.get_current_revision()
|
|
411
|
+
|
|
412
|
+
# Get all revisions from base to head
|
|
413
|
+
_, script_dir = cls._alembic_config()
|
|
414
|
+
revisions = list(script_dir.walk_revisions())
|
|
415
|
+
revisions.reverse() # Reverse to get chronological order (base first)
|
|
416
|
+
|
|
417
|
+
return current_rev, revisions
|
|
418
|
+
|
|
364
419
|
@classmethod
|
|
365
420
|
def alembic_autogenerate(
|
|
366
421
|
cls,
|
reflex/reflex.py
CHANGED
|
@@ -540,6 +540,39 @@ def migrate():
|
|
|
540
540
|
prerequisites.check_schema_up_to_date()
|
|
541
541
|
|
|
542
542
|
|
|
543
|
+
@db_cli.command()
|
|
544
|
+
def status():
|
|
545
|
+
"""Check the status of the database schema."""
|
|
546
|
+
from reflex.model import Model, format_revision
|
|
547
|
+
from reflex.utils import prerequisites
|
|
548
|
+
|
|
549
|
+
prerequisites.get_app()
|
|
550
|
+
if not prerequisites.check_db_initialized():
|
|
551
|
+
console.info(
|
|
552
|
+
"Database is not initialized. Run [bold]reflex db init[/bold] to initialize."
|
|
553
|
+
)
|
|
554
|
+
return
|
|
555
|
+
|
|
556
|
+
# Run alembic check command and display output
|
|
557
|
+
import reflex.config
|
|
558
|
+
|
|
559
|
+
config = reflex.config.get_config()
|
|
560
|
+
console.print(f"[bold]\\[{config.db_url}][/bold]")
|
|
561
|
+
|
|
562
|
+
# Get migration history using Model method
|
|
563
|
+
current_rev, revisions = Model.get_migration_history()
|
|
564
|
+
if current_rev is None and not revisions:
|
|
565
|
+
return
|
|
566
|
+
|
|
567
|
+
current_reached_ref = [current_rev is None]
|
|
568
|
+
|
|
569
|
+
# Show migration history in chronological order
|
|
570
|
+
console.print("<base>")
|
|
571
|
+
for rev in revisions:
|
|
572
|
+
# Format and print the revision
|
|
573
|
+
console.print(format_revision(rev, current_rev, current_reached_ref))
|
|
574
|
+
|
|
575
|
+
|
|
543
576
|
@db_cli.command()
|
|
544
577
|
@click.option(
|
|
545
578
|
"--message",
|
reflex/state.py
CHANGED
|
@@ -1100,7 +1100,12 @@ class BaseState(EvenMoreBasicBaseState):
|
|
|
1100
1100
|
Returns:
|
|
1101
1101
|
The event handler.
|
|
1102
1102
|
"""
|
|
1103
|
-
|
|
1103
|
+
# Check if function has stored event_actions from decorator
|
|
1104
|
+
event_actions = getattr(fn, "_rx_event_actions", {})
|
|
1105
|
+
|
|
1106
|
+
return EventHandler(
|
|
1107
|
+
fn=fn, state_full_name=cls.get_full_name(), event_actions=event_actions
|
|
1108
|
+
)
|
|
1104
1109
|
|
|
1105
1110
|
@classmethod
|
|
1106
1111
|
def _create_setvar(cls):
|
|
@@ -2412,19 +2417,19 @@ class FrontendEventExceptionState(State):
|
|
|
2412
2417
|
"""Substate for handling frontend exceptions."""
|
|
2413
2418
|
|
|
2414
2419
|
@event
|
|
2415
|
-
def handle_frontend_exception(self,
|
|
2420
|
+
def handle_frontend_exception(self, info: str, component_stack: str) -> None:
|
|
2416
2421
|
"""Handle frontend exceptions.
|
|
2417
2422
|
|
|
2418
2423
|
If a frontend exception handler is provided, it will be called.
|
|
2419
2424
|
Otherwise, the default frontend exception handler will be called.
|
|
2420
2425
|
|
|
2421
2426
|
Args:
|
|
2422
|
-
|
|
2427
|
+
info: The exception information.
|
|
2423
2428
|
component_stack: The stack trace of the component where the exception occurred.
|
|
2424
2429
|
|
|
2425
2430
|
"""
|
|
2426
2431
|
prerequisites.get_and_validate_app().app.frontend_exception_handler(
|
|
2427
|
-
Exception(
|
|
2432
|
+
Exception(info)
|
|
2428
2433
|
)
|
|
2429
2434
|
|
|
2430
2435
|
|