Skip to content

Commit c2053fc

Browse files
committed
Merge branch 'exp-memo' of https://github.com/FarhanAliRaza/reflex into exp-memo
2 parents 522e7e5 + a2f4400 commit c2053fc

41 files changed

Lines changed: 792 additions & 633 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

pyi_hashes.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"reflex/components/core/window_events.pyi": "af33ccec866b9540ee7fbec6dbfbd151",
2424
"reflex/components/datadisplay/__init__.pyi": "52755871369acbfd3a96b46b9a11d32e",
2525
"reflex/components/datadisplay/code.pyi": "b86769987ef4d1cbdddb461be88539fd",
26-
"reflex/components/datadisplay/dataeditor.pyi": "f8c1e816c9f22f4a7429f812214407f2",
26+
"reflex/components/datadisplay/dataeditor.pyi": "9b85f3cf6156293cd9961eb17a0ea684",
2727
"reflex/components/datadisplay/shiki_code_block.pyi": "1d53e75b6be0d3385a342e7b3011babd",
2828
"reflex/components/el/__init__.pyi": "0adfd001a926a2a40aee94f6fa725ecc",
2929
"reflex/components/el/element.pyi": "c5974a92fbc310e42d0f6cfdd13472f4",
@@ -39,8 +39,8 @@
3939
"reflex/components/el/elements/tables.pyi": "686eb70ea7d8c4dafb0cc5c284e76184",
4040
"reflex/components/el/elements/typography.pyi": "684e83dde887dba12badd0fb75c87c04",
4141
"reflex/components/gridjs/datatable.pyi": "98a7e1b3f3b60cafcdfcd8879750ee42",
42-
"reflex/components/lucide/icon.pyi": "dcb8773ef162f3ec5759efe11374cf5e",
43-
"reflex/components/markdown/markdown.pyi": "dd74e8e9665b2a813ff799a7aa190b44",
42+
"reflex/components/lucide/icon.pyi": "1db10f2b544908dd20c56ca4bc33d5e0",
43+
"reflex/components/markdown/markdown.pyi": "4c4bca6fb0643abb90aca9c0bb87f722",
4444
"reflex/components/moment/moment.pyi": "e1952f1c2c82cef85d91e970d1be64ab",
4545
"reflex/components/plotly/plotly.pyi": "4311a0aae2abcc9226abb6a273f96372",
4646
"reflex/components/radix/__init__.pyi": "5d8e3579912473e563676bfc71f29191",
@@ -49,7 +49,7 @@
4949
"reflex/components/radix/primitives/base.pyi": "9ef34884fb6028dc017df5e2db639c81",
5050
"reflex/components/radix/primitives/dialog.pyi": "9ee73362bb59619c482b6b0d07033f37",
5151
"reflex/components/radix/primitives/drawer.pyi": "921e45dfaf5b9131ef27c561c3acca2e",
52-
"reflex/components/radix/primitives/form.pyi": "78055e820703c98c3b838aa889566365",
52+
"reflex/components/radix/primitives/form.pyi": "17002a3e9d7f52b3207614f6c1c9a24a",
5353
"reflex/components/radix/primitives/progress.pyi": "c917952d57ddb3e138a40c4005120d5e",
5454
"reflex/components/radix/primitives/slider.pyi": "4ff06f0025d47f166132909b09ab96f8",
5555
"reflex/components/radix/themes/__init__.pyi": "582b4a7ead62b2ae8605e17fa084c063",
@@ -71,7 +71,7 @@
7171
"reflex/components/radix/themes/components/dialog.pyi": "d2615f1a68c80ff930444d054b598c13",
7272
"reflex/components/radix/themes/components/dropdown_menu.pyi": "43f8770c9adf93c73398d68f79048424",
7373
"reflex/components/radix/themes/components/hover_card.pyi": "a96f4433237f9994decf935deff9f269",
74-
"reflex/components/radix/themes/components/icon_button.pyi": "e930911d8ecbe61e5447e61c76a28ab6",
74+
"reflex/components/radix/themes/components/icon_button.pyi": "f12a874bad243a81e5c8740a1d86c6bc",
7575
"reflex/components/radix/themes/components/inset.pyi": "bd7a2186b553bd4c86d83ff50c784066",
7676
"reflex/components/radix/themes/components/popover.pyi": "91f8edefeb232cc6d48690b1838144c2",
7777
"reflex/components/radix/themes/components/progress.pyi": "0e59587d5b3c8fe0d0067587f144e5b0",
@@ -106,7 +106,7 @@
106106
"reflex/components/radix/themes/typography/blockquote.pyi": "080c71899532f5dbf4cf143e7a5ad3bf",
107107
"reflex/components/radix/themes/typography/code.pyi": "7ffe785d55979cf8ff97ea040f3e2b64",
108108
"reflex/components/radix/themes/typography/heading.pyi": "0ebb38915cd0521fd59c569e04d288bb",
109-
"reflex/components/radix/themes/typography/link.pyi": "e88c5d880a54548b6808c097ac62505b",
109+
"reflex/components/radix/themes/typography/link.pyi": "64878125a37d47d676c9adf8156d8c41",
110110
"reflex/components/radix/themes/typography/text.pyi": "50f9ca15a941e4b77ddd12e77aa3c03e",
111111
"reflex/components/react_player/audio.pyi": "0e1690ff1f1f39bc748278d292238350",
112112
"reflex/components/react_player/react_player.pyi": "5ccd373b94ed1d3934ae6afc46bd6fe4",

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "reflex"
3-
version = "0.8.28dev1"
3+
version = "0.9.0dev1"
44
description = "Web apps in pure Python."
55
license.text = "Apache-2.0"
66
authors = [
@@ -244,7 +244,7 @@ fail_fast = true
244244

245245
[[tool.pre-commit.repos]]
246246
repo = "https://github.com/astral-sh/ruff-pre-commit"
247-
rev = "v0.15.1"
247+
rev = "v0.15.6"
248248
hooks = [
249249
{ id = "ruff-format", args = [
250250
"reflex",

reflex/app.py

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from starlette.requests import ClientDisconnect, Request
4343
from starlette.responses import JSONResponse, Response, StreamingResponse
4444
from starlette.staticfiles import StaticFiles
45+
from typing_extensions import Unpack
4546

4647
from reflex import constants
4748
from reflex.admin import AdminDash
@@ -84,6 +85,7 @@
8485
noop,
8586
)
8687
from reflex.experimental.memo import EXPERIMENTAL_MEMOS
88+
from reflex.istate.manager import StateModificationContext
8789
from reflex.istate.proxy import StateProxy
8890
from reflex.page import DECORATED_PAGES
8991
from reflex.route import (
@@ -610,11 +612,11 @@ def __repr__(self) -> str:
610612
def __call__(self) -> ASGIApp:
611613
"""Run the backend api instance.
612614
613-
Raises:
614-
ValueError: If the app has not been initialized.
615-
616615
Returns:
617616
The backend api.
617+
618+
Raises:
619+
ValueError: If the app has not been initialized.
618620
"""
619621
from reflex.assets import remove_stale_external_asset_symlinks
620622
from reflex.vars.base import GLOBAL_CACHE
@@ -929,11 +931,11 @@ def _check_routes_conflict(self, new_route: str):
929931
930932
Based on conflicts that React Router would throw if not intercepted.
931933
932-
Raises:
933-
RouteValueError: exception showing which conflict exist with the route to be added
934-
935934
Args:
936935
new_route: the route being newly added.
936+
937+
Raises:
938+
RouteValueError: exception showing which conflict exist with the route to be added
937939
"""
938940
from reflex.utils.exceptions import RouteValueError
939941

@@ -1575,6 +1577,7 @@ async def modify_state(
15751577
token: str,
15761578
background: bool = False,
15771579
previous_dirty_vars: dict[str, set[str]] | None = None,
1580+
**context: Unpack[StateModificationContext],
15781581
) -> AsyncIterator[BaseState]:
15791582
"""Modify the state out of band.
15801583
@@ -1595,7 +1598,7 @@ async def modify_state(
15951598

15961599
# Get exclusive access to the state.
15971600
async with self.state_manager.modify_state_with_links(
1598-
token, previous_dirty_vars=previous_dirty_vars
1601+
token, previous_dirty_vars=previous_dirty_vars, **context
15991602
) as state:
16001603
# No other event handler can modify the state while in this context.
16011604
yield state
@@ -1628,7 +1631,7 @@ def _process_background(
16281631
if not handler.is_background:
16291632
return None
16301633

1631-
substate = StateProxy(substate)
1634+
substate = StateProxy(substate, event)
16321635

16331636
async def _coro():
16341637
"""Coroutine to process the event and emit updates inside an asyncio.Task.
@@ -1767,11 +1770,11 @@ async def process(
17671770
headers: The client headers.
17681771
client_ip: The client_ip.
17691772
1770-
Raises:
1771-
Exception: If a reflex specific error occurs during processing the event.
1772-
17731773
Yields:
17741774
The state updates after processing the event.
1775+
1776+
Raises:
1777+
Exception: If a reflex specific error occurs during processing the event.
17751778
"""
17761779
from reflex.utils import telemetry
17771780

@@ -2046,7 +2049,7 @@ async def _ndjson_updates():
20462049
"""
20472050
# Process the event.
20482051
async with app.state_manager.modify_state_with_links(
2049-
event.substate_token
2052+
event.substate_token, event=event
20502053
) as state:
20512054
async for update in state._process(event):
20522055
# Postprocess the event.
@@ -2191,14 +2194,12 @@ async def emit_update(self, update: StateUpdate, token: str) -> None:
21912194
async def on_event(self, sid: str, data: Any):
21922195
"""Event for receiving front-end websocket events.
21932196
2194-
Raises:
2195-
RuntimeError: If the Socket.IO is badly initialized.
2196-
21972197
Args:
21982198
sid: The Socket.IO session id.
21992199
data: The event data.
22002200
22012201
Raises:
2202+
RuntimeError: If the Socket.IO is badly initialized.
22022203
EventDeserializationError: If the event data is not a dictionary.
22032204
"""
22042205
fields = data

reflex/assets.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ def asset(
7272
the immediate caller 1. When using rx.asset via a helper function,
7373
increase this number for each helper function in the stack.
7474
75+
Returns:
76+
The relative URL to the asset.
77+
7578
Raises:
7679
FileNotFoundError: If the file does not exist.
7780
ValueError: If subfolder is provided for local assets.
78-
79-
Returns:
80-
The relative URL to the asset.
8181
"""
8282
assets = constants.Dirs.APP_ASSETS
8383
backend_only = EnvironmentVariables.REFLEX_BACKEND_ONLY.get()

reflex/compiler/compiler.py

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
from reflex.utils.exceptions import ReflexError
3434
from reflex.utils.exec import is_prod_mode
3535
from reflex.utils.format import to_title_case
36-
from reflex.utils.imports import ImportVar
36+
from reflex.utils.imports import ImportVar, ParsedImportDict
3737
from reflex.utils.prerequisites import get_web_dir
3838
from reflex.vars.base import LiteralVar, Var
3939

@@ -411,6 +411,47 @@ def _compile_memo_components(
411411
)
412412

413413

414+
def _get_shared_components_recursive(
415+
component: BaseComponent,
416+
rendered_components: dict[str, None],
417+
all_import_dicts: list[ParsedImportDict],
418+
):
419+
"""Get the shared components for a component and its children.
420+
421+
A shared component is a StatefulComponent that appears in 2 or more
422+
pages and is a candidate for writing to a common file and importing
423+
into each page where it is used.
424+
425+
Args:
426+
component: The component to collect shared StatefulComponents for.
427+
rendered_components: A dict to store the rendered shared components in.
428+
all_import_dicts: A list to store the imports of all shared components in.
429+
"""
430+
for child in component.children:
431+
# Depth-first traversal.
432+
_get_shared_components_recursive(child, rendered_components, all_import_dicts)
433+
434+
# When the component is referenced by more than one page, render it
435+
# to be included in the STATEFUL_COMPONENTS module.
436+
# Skip this step in dev mode, thereby avoiding potential hot reload errors for larger apps
437+
if isinstance(component, StatefulComponent) and component.references > 1:
438+
# Reset this flag to render the actual component.
439+
component.rendered_as_shared = False
440+
441+
# Include dynamic imports in the shared component.
442+
if dynamic_imports := component._get_all_dynamic_imports():
443+
rendered_components.update(dict.fromkeys(dynamic_imports))
444+
445+
# Include custom code in the shared component.
446+
rendered_components.update(component._get_all_custom_code(export=True))
447+
448+
# Include all imports in the shared component.
449+
all_import_dicts.append(component._get_all_imports())
450+
451+
# Indicate that this component now imports from the shared file.
452+
component.rendered_as_shared = True
453+
454+
414455
def _compile_stateful_components(
415456
page_components: list[BaseComponent],
416457
) -> str:
@@ -430,46 +471,10 @@ def _compile_stateful_components(
430471
all_import_dicts = []
431472
rendered_components = {}
432473

433-
def get_shared_components_recursive(component: BaseComponent):
434-
"""Get the shared components for a component and its children.
435-
436-
A shared component is a StatefulComponent that appears in 2 or more
437-
pages and is a candidate for writing to a common file and importing
438-
into each page where it is used.
439-
440-
Args:
441-
component: The component to collect shared StatefulComponents for.
442-
"""
443-
for child in component.children:
444-
# Depth-first traversal.
445-
get_shared_components_recursive(child)
446-
447-
# When the component is referenced by more than one page, render it
448-
# to be included in the STATEFUL_COMPONENTS module.
449-
# Skip this step in dev mode, thereby avoiding potential hot reload errors for larger apps
450-
if (
451-
isinstance(component, StatefulComponent)
452-
and component.references > 1
453-
and is_prod_mode()
454-
):
455-
# Reset this flag to render the actual component.
456-
component.rendered_as_shared = False
457-
458-
# Include dynamic imports in the shared component.
459-
if dynamic_imports := component._get_all_dynamic_imports():
460-
rendered_components.update(dict.fromkeys(dynamic_imports))
461-
462-
# Include custom code in the shared component.
463-
rendered_components.update(component._get_all_custom_code(export=True))
464-
465-
# Include all imports in the shared component.
466-
all_import_dicts.append(component._get_all_imports())
467-
468-
# Indicate that this component now imports from the shared file.
469-
component.rendered_as_shared = True
470-
471474
for page_component in page_components:
472-
get_shared_components_recursive(page_component)
475+
_get_shared_components_recursive(
476+
page_component, rendered_components, all_import_dicts
477+
)
473478

474479
# Don't import from the file that we're about to create.
475480
all_imports = utils.merge_imports(*all_import_dicts)
@@ -637,7 +642,7 @@ def compile_stateful_components(
637642
progress_function()
638643
page_components.append(page_component)
639644

640-
code = _compile_stateful_components(page_components)
645+
code = _compile_stateful_components(page_components) if is_prod_mode() else ""
641646
return output_path, code, page_components
642647

643648

reflex/compiler/utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,13 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
4545
Args:
4646
fields: The set of fields to import from the library.
4747
48-
Raises:
49-
ValueError: If there is more than one default import.
50-
5148
Returns:
5249
The libraries for default and rest.
5350
default: default library. When install "import def from library".
5451
rest: rest of libraries. When install "import {rest1, rest2} from library"
52+
53+
Raises:
54+
ValueError: If there is more than one default import.
5555
"""
5656
# ignore the ImportVar fields with render=False during compilation
5757
fields_set = {field for field in fields if field.render}
@@ -111,11 +111,11 @@ def compile_imports(import_dict: ParsedImportDict) -> list[_ImportDict]:
111111
Args:
112112
import_dict: The import dict to compile.
113113
114-
Raises:
115-
ValueError: If an import in the dict is invalid.
116-
117114
Returns:
118115
The list of import dict.
116+
117+
Raises:
118+
ValueError: If an import in the dict is invalid.
119119
"""
120120
collapsed_import_dict: ParsedImportDict = imports.collapse_imports(import_dict)
121121
validate_imports(collapsed_import_dict)

reflex/components/base/meta.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ class Title(elements.Title):
1414
def render(self) -> dict:
1515
"""Render the title component.
1616
17-
Raises:
18-
ValueError: If the title is not a single string.
19-
2017
Returns:
2118
The rendered title component.
19+
20+
Raises:
21+
ValueError: If the title is not a single string.
2222
"""
2323
# Make sure the title is a single string.
2424
if len(self.children) != 1 or not isinstance(self.children[0], Bare):

reflex/components/component.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,11 +1166,11 @@ def _add_style_recursive(
11661166
style: A dict from component to styling.
11671167
theme: The theme to apply. (for retro-compatibility with deprecated _apply_theme API)
11681168
1169-
Raises:
1170-
UserWarning: If `_add_style` has been overridden.
1171-
11721169
Returns:
11731170
The component with the additional style.
1171+
1172+
Raises:
1173+
UserWarning: If `_add_style` has been overridden.
11741174
"""
11751175
# 1. Default style from `_add_style`/`add_style`.
11761176
if type(self)._add_style != Component._add_style:

reflex/components/core/breakpoints.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ def create(
6363
lg: Styling when in the large width
6464
xl: Styling when in the extra-large width
6565
66-
Raises:
67-
ValueError: If both custom and any other named parameters are provided.
68-
6966
Returns:
7067
The responsive mapping.
68+
69+
Raises:
70+
ValueError: If both custom and any other named parameters are provided.
7171
"""
7272
thresholds = [initial, xs, sm, md, lg, xl]
7373

reflex/components/datadisplay/code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ class Theme:
382382
class CodeBlock(Component, MarkdownComponentMap):
383383
"""A code block."""
384384

385-
library = "react-syntax-highlighter@16.1.0"
385+
library = "react-syntax-highlighter@16.1.1"
386386

387387
tag = "PrismAsyncLight"
388388

0 commit comments

Comments
 (0)