Skip to content

Commit fcfe342

Browse files
committed
Merge remote-tracking branch 'origin/main' into lendemor/pointer_events
2 parents 4499d4d + f6998de commit fcfe342

51 files changed

Lines changed: 1713 additions & 1021 deletions

Some content is hidden

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

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,12 @@
1919

2020
---
2121

22-
# Reflex
22+
> [!NOTE]
23+
> 🚀 **Try [Reflex Build](https://build.reflex.dev/)** – our AI-powered app builder that generates full-stack Reflex applications in seconds.
24+
25+
---
26+
27+
# Introduction
2328

2429
Reflex is a library to build full-stack web apps in pure Python.
2530

@@ -227,7 +232,10 @@ You can create a multi-page app by adding more pages.
227232

228233
Reflex launched in December 2022 with the name Pynecone.
229234

230-
Beginning in 2025, [Reflex Cloud](https://cloud.reflex.dev) has launched to provide the best hosting experience for Reflex apps. We will continue to develop it and implement more features.
235+
🚀 Introducing [Reflex Build](https://build.reflex.dev/) — Our AI-Powered Builder
236+
Reflex Build uses AI to generate complete full-stack Python applications. It helps you quickly create, customize, and refine your Reflex apps — from frontend components to backend logic — so you can focus on your ideas instead of boilerplate code. Whether you’re prototyping or scaling, Reflex Build accelerates development by intelligently scaffolding and optimizing your app’s entire stack.
237+
238+
Alongside this, [Reflex Cloud](https://cloud.reflex.dev) launched in 2025 to offer the best hosting experience for your Reflex apps. We’re continuously improving the platform with new features and capabilities.
231239

232240
Reflex has new releases and features coming every week! Make sure to :star: star and :eyes: watch this repository to stay up to date.
233241

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "reflex"
3-
version = "0.7.15dev1"
3+
version = "0.8.0dev1"
44
description = "Web apps in pure Python."
55
license.text = "Apache-2.0"
66
authors = [

reflex/app.py

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@
6868
from reflex.components.core.upload import Upload, get_upload_dir
6969
from reflex.components.radix import themes
7070
from reflex.components.sonner.toast import toast
71-
from reflex.config import ExecutorType, environment, get_config
71+
from reflex.config import get_config
72+
from reflex.environment import ExecutorType, environment
7273
from reflex.event import (
7374
_EVENT_FIELDS,
7475
Event,
@@ -1292,15 +1293,6 @@ def get_compilation_time() -> str:
12921293
# Track imports found.
12931294
all_imports = {}
12941295

1295-
# This has to happen before compiling stateful components as that
1296-
# prevents recursive functions from reaching all components.
1297-
for component in self._pages.values():
1298-
# Add component._get_all_imports() to all_imports.
1299-
all_imports.update(component._get_all_imports())
1300-
1301-
# Add the app wrappers from this component.
1302-
app_wrappers.update(component._get_all_app_wrap_components())
1303-
13041296
if (toaster := self.toaster) is not None:
13051297
from reflex.components.component import memo
13061298

@@ -1318,6 +1310,25 @@ def memoized_toast_provider():
13181310
if component is not None:
13191311
app_wrappers[key] = component
13201312

1313+
# Compile custom components.
1314+
(
1315+
custom_components_output,
1316+
custom_components_result,
1317+
custom_components_imports,
1318+
) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
1319+
compile_results.append((custom_components_output, custom_components_result))
1320+
all_imports.update(custom_components_imports)
1321+
progress.advance(task)
1322+
1323+
# This has to happen before compiling stateful components as that
1324+
# prevents recursive functions from reaching all components.
1325+
for component in self._pages.values():
1326+
# Add component._get_all_imports() to all_imports.
1327+
all_imports.update(component._get_all_imports())
1328+
1329+
# Add the app wrappers from this component.
1330+
app_wrappers.update(component._get_all_app_wrap_components())
1331+
13211332
if self.error_boundary:
13221333
from reflex.compiler.compiler import into_component
13231334

@@ -1464,16 +1475,6 @@ def _submit_work_without_advancing(
14641475
)
14651476
progress.advance(task)
14661477

1467-
# Compile custom components.
1468-
(
1469-
custom_components_output,
1470-
custom_components_result,
1471-
custom_components_imports,
1472-
) = compiler.compile_components(dict.fromkeys(CUSTOM_COMPONENTS.values()))
1473-
compile_results.append((custom_components_output, custom_components_result))
1474-
all_imports.update(custom_components_imports)
1475-
1476-
progress.advance(task)
14771478
progress.stop()
14781479

14791480
if dry_run:

reflex/assets.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from pathlib import Path
55

66
from reflex import constants
7-
from reflex.config import EnvironmentVariables
7+
from reflex.environment import EnvironmentVariables
88

99

1010
def asset(

reflex/compiler/compiler.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
CustomComponent,
2020
StatefulComponent,
2121
)
22-
from reflex.config import environment, get_config
22+
from reflex.config import get_config
2323
from reflex.constants.compiler import PageNames
24+
from reflex.environment import environment
2425
from reflex.state import BaseState
2526
from reflex.style import SYSTEM_COLOR_MODE
2627
from reflex.utils import console, path_ops

reflex/compiler/utils.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
from typing import Any
1212
from urllib.parse import urlparse
1313

14-
from pydantic.v1.fields import ModelField
15-
1614
from reflex import constants
1715
from reflex.components.base import (
1816
Body,
@@ -34,7 +32,7 @@
3432
from reflex.utils.exec import is_in_app_harness
3533
from reflex.utils.imports import ImportVar, ParsedImportDict
3634
from reflex.utils.prerequisites import get_web_dir
37-
from reflex.vars.base import Var
35+
from reflex.vars.base import Field, Var
3836

3937
# To re-export this function.
4038
merge_imports = imports.merge_imports
@@ -212,7 +210,7 @@ def compile_state(state: type[BaseState]) -> dict:
212210

213211

214212
def _compile_client_storage_field(
215-
field: ModelField,
213+
field: Field,
216214
) -> tuple[
217215
type[Cookie] | type[LocalStorage] | type[SessionStorage] | None,
218216
dict[str, Any] | None,

reflex/components/base/bare.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from reflex.components.component import BaseComponent, Component, ComponentStyle
99
from reflex.components.tags import Tag
1010
from reflex.components.tags.tagless import Tagless
11-
from reflex.config import PerformanceMode, environment
11+
from reflex.environment import PerformanceMode, environment
1212
from reflex.utils import console
1313
from reflex.utils.decorator import once
1414
from reflex.utils.imports import ParsedImportDict
@@ -141,17 +141,29 @@ def _get_all_custom_code(self) -> set[str]:
141141
custom_code |= component._get_all_custom_code()
142142
return custom_code
143143

144-
def _get_all_app_wrap_components(self) -> dict[tuple[int, str], Component]:
144+
def _get_all_app_wrap_components(
145+
self, *, ignore_ids: set[int] | None = None
146+
) -> dict[tuple[int, str], Component]:
145147
"""Get the components that should be wrapped in the app.
146148
149+
Args:
150+
ignore_ids: The ids to ignore when collecting components.
151+
147152
Returns:
148153
The components that should be wrapped in the app.
149154
"""
150-
app_wrap_components = super()._get_all_app_wrap_components()
155+
ignore_ids = ignore_ids or set()
156+
app_wrap_components = super()._get_all_app_wrap_components(
157+
ignore_ids=ignore_ids
158+
)
151159
if isinstance(self.contents, Var):
152160
for component in _components_from_var(self.contents):
153-
if isinstance(component, Component):
154-
app_wrap_components |= component._get_all_app_wrap_components()
161+
component_id = id(component)
162+
if isinstance(component, Component) and component_id not in ignore_ids:
163+
ignore_ids.add(component_id)
164+
app_wrap_components |= component._get_all_app_wrap_components(
165+
ignore_ids=ignore_ids
166+
)
155167
return app_wrap_components
156168

157169
def _get_all_refs(self) -> set[str]:

reflex/components/component.py

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import dataclasses
88
import functools
99
import inspect
10-
import sys
1110
import typing
1211
from abc import ABC, ABCMeta, abstractmethod
1312
from collections.abc import Callable, Iterator, Mapping, Sequence
@@ -20,10 +19,8 @@
2019
Annotated,
2120
Any,
2221
ClassVar,
23-
ForwardRef,
2422
Generic,
2523
TypeVar,
26-
_eval_type, # pyright: ignore [reportAttributeAccessIssue]
2724
cast,
2825
get_args,
2926
get_origin,
@@ -75,46 +72,6 @@
7572
from reflex.vars.object import ObjectVar
7673
from reflex.vars.sequence import LiteralArrayVar, LiteralStringVar, StringVar
7774

78-
79-
def resolve_annotations(
80-
raw_annotations: Mapping[str, type[Any]], module_name: str | None
81-
) -> dict[str, type[Any]]:
82-
"""Partially taken from typing.get_type_hints.
83-
84-
Resolve string or ForwardRef annotations into type objects if possible.
85-
86-
Args:
87-
raw_annotations: The raw annotations to resolve.
88-
module_name: The name of the module.
89-
90-
Returns:
91-
The resolved annotations.
92-
"""
93-
module = sys.modules.get(module_name, None) if module_name is not None else None
94-
95-
base_globals: dict[str, Any] | None = (
96-
module.__dict__ if module is not None else None
97-
)
98-
99-
annotations = {}
100-
for name, value in raw_annotations.items():
101-
if isinstance(value, str):
102-
if sys.version_info == (3, 10, 0):
103-
value = ForwardRef(value, is_argument=False)
104-
else:
105-
value = ForwardRef(value, is_argument=False, is_class=True)
106-
try:
107-
if sys.version_info >= (3, 13):
108-
value = _eval_type(value, base_globals, None, type_params=())
109-
else:
110-
value = _eval_type(value, base_globals, None)
111-
except NameError:
112-
# this is ok, it can be fixed with update_forward_refs
113-
pass
114-
annotations[name] = value
115-
return annotations
116-
117-
11875
FIELD_TYPE = TypeVar("FIELD_TYPE")
11976

12077

@@ -229,7 +186,7 @@ def __new__(cls, name: str, bases: tuple[type], namespace: dict[str, Any]) -> ty
229186
# Add the field to the class
230187
inherited_fields: dict[str, ComponentField] = {}
231188
own_fields: dict[str, ComponentField] = {}
232-
resolved_annotations = resolve_annotations(
189+
resolved_annotations = types.resolve_annotations(
233190
namespace.get("__annotations__", {}), namespace["__module__"]
234191
)
235192

@@ -908,9 +865,8 @@ def _post_init(self, *args, **kwargs):
908865
for key, value in kwargs.items():
909866
setattr(self, key, value)
910867

911-
def get_event_triggers(
912-
self,
913-
) -> dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]:
868+
@classmethod
869+
def get_event_triggers(cls) -> dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]:
914870
"""Get the event triggers for the component.
915871
916872
Returns:
@@ -927,7 +883,7 @@ def get_event_triggers(
927883
)
928884
else no_args_event_spec
929885
)
930-
for name, field in self.get_fields().items()
886+
for name, field in cls.get_fields().items()
931887
if field.type_origin is EventHandler
932888
} # pyright: ignore [reportOperatorIssue]
933889

@@ -1977,24 +1933,38 @@ def _get_app_wrap_components() -> dict[tuple[int, str], Component]:
19771933
"""
19781934
return {}
19791935

1980-
def _get_all_app_wrap_components(self) -> dict[tuple[int, str], Component]:
1936+
def _get_all_app_wrap_components(
1937+
self, *, ignore_ids: set[int] | None = None
1938+
) -> dict[tuple[int, str], Component]:
19811939
"""Get the app wrap components for the component and its children.
19821940
1941+
Args:
1942+
ignore_ids: A set of component IDs to ignore. Used to avoid duplicates.
1943+
19831944
Returns:
19841945
The app wrap components.
19851946
"""
1947+
ignore_ids = ignore_ids or set()
19861948
# Store the components in a set to avoid duplicates.
19871949
components = self._get_app_wrap_components()
19881950

19891951
for component in tuple(components.values()):
1990-
components.update(component._get_all_app_wrap_components())
1952+
component_id = id(component)
1953+
if component_id in ignore_ids:
1954+
continue
1955+
ignore_ids.add(component_id)
1956+
components.update(
1957+
component._get_all_app_wrap_components(ignore_ids=ignore_ids)
1958+
)
19911959

19921960
# Add the app wrap components for the children.
19931961
for child in self.children:
1962+
child_id = id(child)
19941963
# Skip BaseComponent and StatefulComponent children.
1995-
if not isinstance(child, Component):
1964+
if not isinstance(child, Component) or child_id in ignore_ids:
19961965
continue
1997-
components.update(child._get_all_app_wrap_components())
1966+
ignore_ids.add(child_id)
1967+
components.update(child._get_all_app_wrap_components(ignore_ids=ignore_ids))
19981968

19991969
# Return the components.
20001970
return components
@@ -2207,6 +2177,24 @@ def get_component(self) -> Component:
22072177
component._add_style_recursive(style)
22082178
return component
22092179

2180+
def _get_all_app_wrap_components(
2181+
self, *, ignore_ids: set[int] | None = None
2182+
) -> dict[tuple[int, str], Component]:
2183+
"""Get the app wrap components for the custom component.
2184+
2185+
Args:
2186+
ignore_ids: A set of IDs to ignore to avoid infinite recursion.
2187+
2188+
Returns:
2189+
The app wrap components.
2190+
"""
2191+
ignore_ids = ignore_ids or set()
2192+
component = self.get_component()
2193+
if id(component) in ignore_ids:
2194+
return {}
2195+
ignore_ids.add(id(component))
2196+
return self.get_component()._get_all_app_wrap_components(ignore_ids=ignore_ids)
2197+
22102198

22112199
CUSTOM_COMPONENTS: dict[str, CustomComponent] = {}
22122200

reflex/components/core/banner.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from reflex.components.radix.themes.layout.flex import Flex
1717
from reflex.components.radix.themes.typography.text import Text
1818
from reflex.components.sonner.toast import ToastProps, toast_ref
19-
from reflex.config import environment
2019
from reflex.constants import Dirs, Hooks, Imports
2120
from reflex.constants.compiler import CompileVars
21+
from reflex.environment import environment
2222
from reflex.utils.imports import ImportVar
2323
from reflex.vars import VarData
2424
from reflex.vars.base import LiteralVar, Var

reflex/components/core/upload.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@
1616
from reflex.components.core.cond import cond
1717
from reflex.components.el.elements.forms import Input
1818
from reflex.components.radix.themes.layout.box import Box
19-
from reflex.config import environment
2019
from reflex.constants import Dirs
2120
from reflex.constants.compiler import Hooks, Imports
21+
from reflex.environment import environment
2222
from reflex.event import (
2323
CallableEventSpec,
2424
EventChain,

0 commit comments

Comments
 (0)