reflex 0.6.8a1__py3-none-any.whl → 0.7.0a1__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/jinja/custom_components/pyproject.toml.jinja2 +1 -1
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +7 -7
- reflex/.templates/jinja/web/pages/utils.js.jinja2 +2 -2
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -4
- reflex/.templates/web/utils/state.js +65 -36
- reflex/__init__.py +4 -17
- reflex/__init__.pyi +1 -2
- reflex/app.py +244 -115
- reflex/app_mixins/lifespan.py +9 -9
- reflex/app_mixins/middleware.py +6 -6
- reflex/app_module_for_backend.py +3 -7
- reflex/base.py +7 -7
- reflex/compiler/compiler.py +8 -0
- reflex/compiler/utils.py +35 -6
- reflex/components/base/bare.py +1 -1
- reflex/components/base/error_boundary.py +2 -1
- reflex/components/base/error_boundary.pyi +2 -1
- reflex/components/base/meta.py +2 -2
- reflex/components/base/strict_mode.py +10 -0
- reflex/components/base/strict_mode.pyi +57 -0
- reflex/components/component.py +38 -77
- reflex/components/core/banner.py +83 -4
- reflex/components/core/banner.pyi +86 -0
- reflex/components/core/breakpoints.py +3 -1
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/client_side_routing.pyi +1 -1
- reflex/components/core/cond.py +9 -10
- reflex/components/core/debounce.py +1 -1
- reflex/components/core/foreach.py +23 -3
- reflex/components/core/html.py +1 -1
- reflex/components/core/match.py +5 -5
- reflex/components/core/sticky.py +160 -0
- reflex/components/core/sticky.pyi +449 -0
- reflex/components/core/upload.py +2 -2
- reflex/components/datadisplay/code.py +5 -14
- reflex/components/datadisplay/dataeditor.py +7 -4
- reflex/components/datadisplay/logo.py +13 -8
- reflex/components/datadisplay/shiki_code_block.py +14 -9
- reflex/components/dynamic.py +22 -3
- reflex/components/el/constants/reflex.py +1 -1
- reflex/components/el/element.py +1 -1
- reflex/components/el/elements/forms.py +4 -4
- reflex/components/el/elements/forms.pyi +4 -4
- reflex/components/lucide/icon.py +46 -8
- reflex/components/lucide/icon.pyi +54 -0
- reflex/components/markdown/markdown.py +10 -8
- reflex/components/moment/moment.py +2 -2
- reflex/components/next/image.py +16 -4
- reflex/components/next/image.pyi +4 -2
- reflex/components/next/link.py +1 -1
- reflex/components/plotly/plotly.py +5 -5
- reflex/components/props.py +3 -3
- reflex/components/radix/__init__.pyi +1 -1
- reflex/components/radix/primitives/accordion.py +9 -5
- reflex/components/radix/primitives/accordion.pyi +3 -1
- reflex/components/radix/primitives/drawer.py +5 -2
- reflex/components/radix/primitives/drawer.pyi +4 -4
- reflex/components/radix/primitives/form.pyi +6 -6
- reflex/components/radix/primitives/progress.py +1 -1
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/themes/color_mode.py +11 -9
- reflex/components/radix/themes/components/alert_dialog.py +3 -0
- reflex/components/radix/themes/components/card.py +1 -1
- reflex/components/radix/themes/components/card.pyi +1 -1
- reflex/components/radix/themes/components/context_menu.py +5 -0
- reflex/components/radix/themes/components/dialog.py +3 -0
- reflex/components/radix/themes/components/dropdown_menu.py +5 -0
- reflex/components/radix/themes/components/hover_card.py +3 -0
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/icon_button.pyi +1 -0
- reflex/components/radix/themes/components/popover.py +3 -0
- reflex/components/radix/themes/components/radio_cards.py +2 -0
- reflex/components/radix/themes/components/radio_group.py +1 -1
- reflex/components/radix/themes/components/select.py +3 -0
- reflex/components/radix/themes/components/tabs.py +3 -0
- reflex/components/radix/themes/components/text_area.py +12 -0
- reflex/components/radix/themes/components/text_area.pyi +2 -0
- reflex/components/radix/themes/components/text_field.py +1 -1
- reflex/components/radix/themes/components/tooltip.py +3 -1
- reflex/components/radix/themes/components/tooltip.pyi +1 -0
- reflex/components/radix/themes/layout/__init__.pyi +1 -1
- reflex/components/radix/themes/layout/list.py +2 -2
- reflex/components/radix/themes/layout/stack.py +2 -2
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/radix/themes/typography/text.py +2 -2
- reflex/components/react_player/react_player.py +1 -1
- reflex/components/recharts/__init__.py +2 -0
- reflex/components/recharts/__init__.pyi +2 -0
- reflex/components/recharts/charts.py +15 -15
- reflex/components/recharts/general.py +19 -4
- reflex/components/recharts/general.pyi +55 -4
- reflex/components/recharts/polar.py +2 -2
- reflex/components/recharts/recharts.py +4 -4
- reflex/components/sonner/toast.py +15 -13
- reflex/components/sonner/toast.pyi +6 -6
- reflex/components/suneditor/editor.py +6 -4
- reflex/components/suneditor/editor.pyi +2 -2
- reflex/components/tags/iter_tag.py +3 -3
- reflex/components/tags/tag.py +25 -3
- reflex/config.py +48 -20
- reflex/constants/__init__.py +1 -0
- reflex/constants/base.py +4 -1
- reflex/constants/compiler.py +5 -2
- reflex/constants/config.py +8 -1
- reflex/constants/installer.py +9 -9
- reflex/constants/style.py +1 -1
- reflex/custom_components/custom_components.py +9 -7
- reflex/event.py +137 -163
- reflex/experimental/__init__.py +19 -11
- reflex/experimental/client_state.py +53 -28
- reflex/experimental/hooks.py +5 -5
- reflex/experimental/layout.py +8 -5
- reflex/experimental/layout.pyi +1 -1
- reflex/experimental/misc.py +3 -3
- reflex/istate/wrappers.py +1 -1
- reflex/middleware/hydrate_middleware.py +2 -2
- reflex/model.py +11 -6
- reflex/page.py +3 -3
- reflex/reflex.py +90 -19
- reflex/route.py +1 -1
- reflex/state.py +358 -401
- reflex/style.py +27 -3
- reflex/testing.py +34 -39
- reflex/utils/build.py +6 -2
- reflex/utils/codespaces.py +1 -4
- reflex/utils/compat.py +6 -5
- reflex/utils/console.py +52 -21
- reflex/utils/exceptions.py +76 -26
- reflex/utils/exec.py +69 -74
- reflex/utils/export.py +6 -1
- reflex/utils/format.py +7 -39
- reflex/utils/imports.py +2 -2
- reflex/utils/lazy_loader.py +7 -1
- reflex/utils/path_ops.py +28 -14
- reflex/utils/prerequisites.py +324 -65
- reflex/utils/processes.py +45 -32
- reflex/utils/pyi_generator.py +30 -25
- reflex/utils/registry.py +4 -4
- reflex/utils/serializers.py +1 -1
- reflex/utils/telemetry.py +5 -4
- reflex/utils/types.py +42 -18
- reflex/vars/base.py +650 -333
- reflex/vars/datetime.py +6 -7
- reflex/vars/dep_tracking.py +344 -0
- reflex/vars/function.py +11 -5
- reflex/vars/number.py +31 -43
- reflex/vars/object.py +63 -62
- reflex/vars/sequence.py +79 -67
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/METADATA +7 -10
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/RECORD +153 -150
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/WHEEL +1 -1
- reflex/experimental/assets.py +0 -37
- reflex/proxy.py +0 -119
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/LICENSE +0 -0
- {reflex-0.6.8a1.dist-info → reflex-0.7.0a1.dist-info}/entry_points.txt +0 -0
reflex/style.py
CHANGED
|
@@ -78,7 +78,7 @@ def set_color_mode(
|
|
|
78
78
|
_var_data=VarData.merge(
|
|
79
79
|
base_setter._get_all_var_data(), new_color_mode._get_all_var_data()
|
|
80
80
|
),
|
|
81
|
-
).to(FunctionVar, EventChain)
|
|
81
|
+
).to(FunctionVar, EventChain)
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
# Var resolves to the current color mode for the app ("light", "dark" or "system")
|
|
@@ -182,7 +182,9 @@ def convert(
|
|
|
182
182
|
var_data = None # Track import/hook data from any Vars in the style dict.
|
|
183
183
|
out = {}
|
|
184
184
|
|
|
185
|
-
def update_out_dict(
|
|
185
|
+
def update_out_dict(
|
|
186
|
+
return_value: Var | dict | list | str, keys_to_update: tuple[str, ...]
|
|
187
|
+
):
|
|
186
188
|
for k in keys_to_update:
|
|
187
189
|
out[k] = return_value
|
|
188
190
|
|
|
@@ -287,9 +289,31 @@ class Style(dict):
|
|
|
287
289
|
_var = LiteralVar.create(value)
|
|
288
290
|
if _var is not None:
|
|
289
291
|
# Carry the imports/hooks when setting a Var as a value.
|
|
290
|
-
self._var_data = VarData.merge(
|
|
292
|
+
self._var_data = VarData.merge(
|
|
293
|
+
getattr(self, "_var_data", None), _var._get_all_var_data()
|
|
294
|
+
)
|
|
291
295
|
super().__setitem__(key, value)
|
|
292
296
|
|
|
297
|
+
def __or__(self, other: Style | dict) -> Style:
|
|
298
|
+
"""Combine two styles.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
other: The other style to combine.
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
The combined style.
|
|
305
|
+
"""
|
|
306
|
+
other_var_data = None
|
|
307
|
+
if not isinstance(other, Style):
|
|
308
|
+
other_dict, other_var_data = convert(other)
|
|
309
|
+
else:
|
|
310
|
+
other_dict, other_var_data = other, other._var_data
|
|
311
|
+
|
|
312
|
+
new_style = Style(super().__or__(other_dict))
|
|
313
|
+
if self._var_data or other_var_data:
|
|
314
|
+
new_style._var_data = VarData.merge(self._var_data, other_var_data)
|
|
315
|
+
return new_style
|
|
316
|
+
|
|
293
317
|
|
|
294
318
|
def _format_emotion_style_pseudo_selector(key: str) -> str:
|
|
295
319
|
"""Format a pseudo selector for emotion CSS-in-JS.
|
reflex/testing.py
CHANGED
|
@@ -44,7 +44,6 @@ import reflex.utils.format
|
|
|
44
44
|
import reflex.utils.prerequisites
|
|
45
45
|
import reflex.utils.processes
|
|
46
46
|
from reflex.config import environment
|
|
47
|
-
from reflex.proxy import proxy_middleware
|
|
48
47
|
from reflex.state import (
|
|
49
48
|
BaseState,
|
|
50
49
|
StateManager,
|
|
@@ -81,17 +80,17 @@ T = TypeVar("T")
|
|
|
81
80
|
TimeoutType = Optional[Union[int, float]]
|
|
82
81
|
|
|
83
82
|
if platform.system() == "Windows":
|
|
84
|
-
FRONTEND_POPEN_ARGS["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP #
|
|
83
|
+
FRONTEND_POPEN_ARGS["creationflags"] = subprocess.CREATE_NEW_PROCESS_GROUP # pyright: ignore [reportAttributeAccessIssue]
|
|
85
84
|
FRONTEND_POPEN_ARGS["shell"] = True
|
|
86
85
|
else:
|
|
87
86
|
FRONTEND_POPEN_ARGS["start_new_session"] = True
|
|
88
87
|
|
|
89
88
|
|
|
90
89
|
# borrowed from py3.11
|
|
91
|
-
class chdir(contextlib.AbstractContextManager):
|
|
90
|
+
class chdir(contextlib.AbstractContextManager): # noqa: N801
|
|
92
91
|
"""Non thread-safe context manager to change the current working directory."""
|
|
93
92
|
|
|
94
|
-
def __init__(self, path):
|
|
93
|
+
def __init__(self, path: str | Path):
|
|
95
94
|
"""Prepare contextmanager.
|
|
96
95
|
|
|
97
96
|
Args:
|
|
@@ -259,7 +258,7 @@ class AppHarness:
|
|
|
259
258
|
if self.app_source is not None:
|
|
260
259
|
app_globals = self._get_globals_from_signature(self.app_source)
|
|
261
260
|
if isinstance(self.app_source, functools.partial):
|
|
262
|
-
self.app_source = self.app_source.func
|
|
261
|
+
self.app_source = self.app_source.func
|
|
263
262
|
# get the source from a function or module object
|
|
264
263
|
source_code = "\n".join(
|
|
265
264
|
[
|
|
@@ -283,6 +282,7 @@ class AppHarness:
|
|
|
283
282
|
before_decorated_pages = reflex.app.DECORATED_PAGES[self.app_name].copy()
|
|
284
283
|
# Ensure the AppHarness test does not skip State assignment due to running via pytest
|
|
285
284
|
os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None)
|
|
285
|
+
os.environ[reflex.constants.APP_HARNESS_FLAG] = "true"
|
|
286
286
|
self.app_module = reflex.utils.prerequisites.get_compiled_app(
|
|
287
287
|
# Do not reload the module for pre-existing apps (only apps generated from source)
|
|
288
288
|
reload=self.app_source is not None
|
|
@@ -294,14 +294,15 @@ class AppHarness:
|
|
|
294
294
|
if p not in before_decorated_pages
|
|
295
295
|
]
|
|
296
296
|
self.app_instance = self.app_module.app
|
|
297
|
-
if
|
|
297
|
+
if self.app_instance and isinstance(
|
|
298
|
+
self.app_instance._state_manager, StateManagerRedis
|
|
299
|
+
):
|
|
298
300
|
# Create our own redis connection for testing.
|
|
299
|
-
self.state_manager = StateManagerRedis.create(self.app_instance.
|
|
301
|
+
self.state_manager = StateManagerRedis.create(self.app_instance._state) # pyright: ignore [reportArgumentType]
|
|
300
302
|
else:
|
|
301
|
-
self.state_manager =
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
self.app_instance.lifespan_tasks.remove(proxy_middleware)
|
|
303
|
+
self.state_manager = (
|
|
304
|
+
self.app_instance._state_manager if self.app_instance else None
|
|
305
|
+
)
|
|
305
306
|
|
|
306
307
|
def _reload_state_module(self):
|
|
307
308
|
"""Reload the rx.State module to avoid conflict when reloading."""
|
|
@@ -326,8 +327,8 @@ class AppHarness:
|
|
|
326
327
|
|
|
327
328
|
return _shutdown_redis
|
|
328
329
|
|
|
329
|
-
def _start_backend(self, port=0):
|
|
330
|
-
if self.app_instance is None:
|
|
330
|
+
def _start_backend(self, port: int = 0):
|
|
331
|
+
if self.app_instance is None or self.app_instance.api is None:
|
|
331
332
|
raise RuntimeError("App was not initialized.")
|
|
332
333
|
self.backend = uvicorn.Server(
|
|
333
334
|
uvicorn.Config(
|
|
@@ -356,12 +357,12 @@ class AppHarness:
|
|
|
356
357
|
self.app_instance.state_manager,
|
|
357
358
|
StateManagerRedis,
|
|
358
359
|
)
|
|
359
|
-
and self.app_instance.
|
|
360
|
+
and self.app_instance._state is not None
|
|
360
361
|
):
|
|
361
362
|
with contextlib.suppress(RuntimeError):
|
|
362
363
|
await self.app_instance.state_manager.close()
|
|
363
364
|
self.app_instance._state_manager = StateManagerRedis.create(
|
|
364
|
-
state=self.app_instance.
|
|
365
|
+
state=self.app_instance._state,
|
|
365
366
|
)
|
|
366
367
|
if not isinstance(self.app_instance.state_manager, StateManagerRedis):
|
|
367
368
|
raise RuntimeError("Failed to reset state manager.")
|
|
@@ -369,12 +370,9 @@ class AppHarness:
|
|
|
369
370
|
def _start_frontend(self):
|
|
370
371
|
# Set up the frontend.
|
|
371
372
|
with chdir(self.app_path):
|
|
372
|
-
backend_host, backend_port = self._poll_for_servers().getsockname()
|
|
373
373
|
config = reflex.config.get_config()
|
|
374
|
-
config.backend_port = backend_port
|
|
375
374
|
config.api_url = "http://{0}:{1}".format(
|
|
376
|
-
|
|
377
|
-
backend_port,
|
|
375
|
+
*self._poll_for_servers().getsockname(),
|
|
378
376
|
)
|
|
379
377
|
reflex.utils.build.setup_frontend(self.app_path)
|
|
380
378
|
|
|
@@ -399,7 +397,6 @@ class AppHarness:
|
|
|
399
397
|
self.frontend_url = m.group(1)
|
|
400
398
|
config = reflex.config.get_config()
|
|
401
399
|
config.deploy_url = self.frontend_url
|
|
402
|
-
config.frontend_port = int(self.frontend_url.rpartition(":")[2])
|
|
403
400
|
break
|
|
404
401
|
if self.frontend_url is None:
|
|
405
402
|
raise RuntimeError("Frontend did not start")
|
|
@@ -433,7 +430,7 @@ class AppHarness:
|
|
|
433
430
|
return self
|
|
434
431
|
|
|
435
432
|
@staticmethod
|
|
436
|
-
def get_app_global_source(key, value):
|
|
433
|
+
def get_app_global_source(key: str, value: Any):
|
|
437
434
|
"""Get the source code of a global object.
|
|
438
435
|
If value is a function or class we render the actual
|
|
439
436
|
source of value otherwise we assign value to key.
|
|
@@ -628,23 +625,23 @@ class AppHarness:
|
|
|
628
625
|
want_headless = True
|
|
629
626
|
if driver_clz is None:
|
|
630
627
|
requested_driver = environment.APP_HARNESS_DRIVER.get()
|
|
631
|
-
driver_clz = getattr(webdriver, requested_driver)
|
|
628
|
+
driver_clz = getattr(webdriver, requested_driver) # pyright: ignore [reportPossiblyUnboundVariable]
|
|
632
629
|
if driver_options is None:
|
|
633
|
-
driver_options = getattr(webdriver, f"{requested_driver}Options")()
|
|
634
|
-
if driver_clz is webdriver.Chrome:
|
|
630
|
+
driver_options = getattr(webdriver, f"{requested_driver}Options")() # pyright: ignore [reportPossiblyUnboundVariable]
|
|
631
|
+
if driver_clz is webdriver.Chrome: # pyright: ignore [reportPossiblyUnboundVariable]
|
|
635
632
|
if driver_options is None:
|
|
636
|
-
driver_options = webdriver.ChromeOptions()
|
|
633
|
+
driver_options = webdriver.ChromeOptions() # pyright: ignore [reportPossiblyUnboundVariable]
|
|
637
634
|
driver_options.add_argument("--class=AppHarness")
|
|
638
635
|
if want_headless:
|
|
639
636
|
driver_options.add_argument("--headless=new")
|
|
640
|
-
elif driver_clz is webdriver.Firefox:
|
|
637
|
+
elif driver_clz is webdriver.Firefox: # pyright: ignore [reportPossiblyUnboundVariable]
|
|
641
638
|
if driver_options is None:
|
|
642
|
-
driver_options = webdriver.FirefoxOptions()
|
|
639
|
+
driver_options = webdriver.FirefoxOptions() # pyright: ignore [reportPossiblyUnboundVariable]
|
|
643
640
|
if want_headless:
|
|
644
641
|
driver_options.add_argument("-headless")
|
|
645
|
-
elif driver_clz is webdriver.Edge:
|
|
642
|
+
elif driver_clz is webdriver.Edge: # pyright: ignore [reportPossiblyUnboundVariable]
|
|
646
643
|
if driver_options is None:
|
|
647
|
-
driver_options = webdriver.EdgeOptions()
|
|
644
|
+
driver_options = webdriver.EdgeOptions() # pyright: ignore [reportPossiblyUnboundVariable]
|
|
648
645
|
if want_headless:
|
|
649
646
|
driver_options.add_argument("headless")
|
|
650
647
|
if driver_options is None:
|
|
@@ -660,7 +657,7 @@ class AppHarness:
|
|
|
660
657
|
driver_options.set_capability(key, value)
|
|
661
658
|
if driver_kwargs is None:
|
|
662
659
|
driver_kwargs = {}
|
|
663
|
-
driver = driver_clz(options=driver_options, **driver_kwargs) #
|
|
660
|
+
driver = driver_clz(options=driver_options, **driver_kwargs) # pyright: ignore [reportOptionalCall, reportArgumentType]
|
|
664
661
|
driver.get(self.frontend_url)
|
|
665
662
|
self._frontends.append(driver)
|
|
666
663
|
return driver
|
|
@@ -892,8 +889,8 @@ class Subdir404TCPServer(socketserver.TCPServer):
|
|
|
892
889
|
request,
|
|
893
890
|
client_address,
|
|
894
891
|
self,
|
|
895
|
-
directory=str(self.root), #
|
|
896
|
-
error_page_map=self.error_page_map, #
|
|
892
|
+
directory=str(self.root), # pyright: ignore [reportCallIssue]
|
|
893
|
+
error_page_map=self.error_page_map, # pyright: ignore [reportCallIssue]
|
|
897
894
|
)
|
|
898
895
|
|
|
899
896
|
|
|
@@ -923,26 +920,24 @@ class AppHarnessProd(AppHarness):
|
|
|
923
920
|
root=web_root,
|
|
924
921
|
error_page_map=error_page_map,
|
|
925
922
|
) as self.frontend_server:
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
923
|
+
self.frontend_url = "http://localhost:{1}".format(
|
|
924
|
+
*self.frontend_server.socket.getsockname()
|
|
925
|
+
)
|
|
929
926
|
self.frontend_server.serve_forever()
|
|
930
927
|
|
|
931
928
|
def _start_frontend(self):
|
|
932
929
|
# Set up the frontend.
|
|
933
930
|
with chdir(self.app_path):
|
|
934
|
-
backend_host, backend_port = self._poll_for_servers().getsockname()
|
|
935
931
|
config = reflex.config.get_config()
|
|
936
|
-
config.backend_port = backend_port
|
|
937
932
|
config.api_url = "http://{0}:{1}".format(
|
|
938
|
-
|
|
939
|
-
backend_port,
|
|
933
|
+
*self._poll_for_servers().getsockname(),
|
|
940
934
|
)
|
|
941
935
|
reflex.reflex.export(
|
|
942
936
|
zipping=False,
|
|
943
937
|
frontend=True,
|
|
944
938
|
backend=False,
|
|
945
939
|
loglevel=reflex.constants.LogLevel.INFO,
|
|
940
|
+
env=reflex.constants.Env.PROD,
|
|
946
941
|
)
|
|
947
942
|
|
|
948
943
|
self.frontend_thread = threading.Thread(target=self._run_frontend)
|
reflex/utils/build.py
CHANGED
|
@@ -13,17 +13,21 @@ from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
|
13
13
|
from reflex import constants
|
|
14
14
|
from reflex.config import get_config
|
|
15
15
|
from reflex.utils import console, path_ops, prerequisites, processes
|
|
16
|
+
from reflex.utils.exec import is_in_app_harness
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
def set_env_json():
|
|
19
20
|
"""Write the upload url to a REFLEX_JSON."""
|
|
20
21
|
path_ops.update_json_file(
|
|
21
22
|
str(prerequisites.get_web_dir() / constants.Dirs.ENV_JSON),
|
|
22
|
-
{
|
|
23
|
+
{
|
|
24
|
+
**{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
|
|
25
|
+
"TEST_MODE": is_in_app_harness(),
|
|
26
|
+
},
|
|
23
27
|
)
|
|
24
28
|
|
|
25
29
|
|
|
26
|
-
def generate_sitemap_config(deploy_url: str, export=False):
|
|
30
|
+
def generate_sitemap_config(deploy_url: str, export: bool = False):
|
|
27
31
|
"""Generate the sitemap config file.
|
|
28
32
|
|
|
29
33
|
Args:
|
reflex/utils/codespaces.py
CHANGED
|
@@ -42,10 +42,7 @@ def codespaces_port_forwarding_domain() -> str | None:
|
|
|
42
42
|
Returns:
|
|
43
43
|
The domain for port forwarding in Github Codespaces, or None if not running in Codespaces.
|
|
44
44
|
"""
|
|
45
|
-
|
|
46
|
-
"GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
|
|
47
|
-
)
|
|
48
|
-
return GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN
|
|
45
|
+
return os.getenv("GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN")
|
|
49
46
|
|
|
50
47
|
|
|
51
48
|
def is_running_in_codespaces() -> bool:
|
reflex/utils/compat.py
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import contextlib
|
|
4
4
|
import sys
|
|
5
|
+
from typing import Any
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
async def windows_hot_reload_lifespan_hack():
|
|
@@ -50,11 +51,11 @@ def pydantic_v1_patch():
|
|
|
50
51
|
]
|
|
51
52
|
originals = {module: sys.modules.get(module) for module in patched_modules}
|
|
52
53
|
try:
|
|
53
|
-
import pydantic.v1
|
|
54
|
+
import pydantic.v1
|
|
54
55
|
|
|
55
|
-
sys.modules["pydantic.fields"] = pydantic.v1.fields #
|
|
56
|
-
sys.modules["pydantic.main"] = pydantic.v1.main #
|
|
57
|
-
sys.modules["pydantic.errors"] = pydantic.v1.errors #
|
|
56
|
+
sys.modules["pydantic.fields"] = pydantic.v1.fields # pyright: ignore [reportAttributeAccessIssue]
|
|
57
|
+
sys.modules["pydantic.main"] = pydantic.v1.main # pyright: ignore [reportAttributeAccessIssue]
|
|
58
|
+
sys.modules["pydantic.errors"] = pydantic.v1.errors # pyright: ignore [reportAttributeAccessIssue]
|
|
58
59
|
sys.modules["pydantic"] = pydantic.v1
|
|
59
60
|
yield
|
|
60
61
|
except (ImportError, AttributeError):
|
|
@@ -74,7 +75,7 @@ with pydantic_v1_patch():
|
|
|
74
75
|
import sqlmodel as sqlmodel
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
def sqlmodel_field_has_primary_key(field) -> bool:
|
|
78
|
+
def sqlmodel_field_has_primary_key(field: Any) -> bool:
|
|
78
79
|
"""Determines if a field is a priamary.
|
|
79
80
|
|
|
80
81
|
Args:
|
reflex/utils/console.py
CHANGED
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import inspect
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from types import FrameType
|
|
6
9
|
|
|
7
10
|
from rich.console import Console
|
|
8
11
|
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
|
|
@@ -14,7 +17,7 @@ from reflex.constants import LogLevel
|
|
|
14
17
|
_console = Console()
|
|
15
18
|
|
|
16
19
|
# The current log level.
|
|
17
|
-
_LOG_LEVEL = LogLevel.
|
|
20
|
+
_LOG_LEVEL = LogLevel.INFO
|
|
18
21
|
|
|
19
22
|
# Deprecated features who's warning has been printed.
|
|
20
23
|
_EMITTED_DEPRECATION_WARNINGS = set()
|
|
@@ -48,24 +51,13 @@ def set_log_level(log_level: LogLevel):
|
|
|
48
51
|
log_level: The log level to set.
|
|
49
52
|
|
|
50
53
|
Raises:
|
|
51
|
-
|
|
54
|
+
TypeError: If the log level is a string.
|
|
52
55
|
"""
|
|
53
56
|
if not isinstance(log_level, LogLevel):
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
reason="use reflex.constants.LogLevel enum instead",
|
|
57
|
-
deprecation_version="0.6.6",
|
|
58
|
-
removal_version="0.7.0",
|
|
57
|
+
raise TypeError(
|
|
58
|
+
f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
|
|
59
59
|
)
|
|
60
|
-
try:
|
|
61
|
-
log_level = getattr(LogLevel, log_level.upper())
|
|
62
|
-
except AttributeError as ae:
|
|
63
|
-
raise ValueError(f"Invalid log level: {log_level}") from ae
|
|
64
|
-
|
|
65
60
|
global _LOG_LEVEL
|
|
66
|
-
if log_level != _LOG_LEVEL:
|
|
67
|
-
# Set the loglevel persistently for subprocesses
|
|
68
|
-
os.environ["LOGLEVEL"] = log_level.value
|
|
69
61
|
_LOG_LEVEL = log_level
|
|
70
62
|
|
|
71
63
|
|
|
@@ -193,6 +185,33 @@ def warn(msg: str, dedupe: bool = False, **kwargs):
|
|
|
193
185
|
print(f"[orange1]Warning: {msg}[/orange1]", **kwargs)
|
|
194
186
|
|
|
195
187
|
|
|
188
|
+
def _get_first_non_framework_frame() -> FrameType | None:
|
|
189
|
+
import click
|
|
190
|
+
import typer
|
|
191
|
+
import typing_extensions
|
|
192
|
+
|
|
193
|
+
import reflex as rx
|
|
194
|
+
|
|
195
|
+
# Exclude utility modules that should never be the source of deprecated reflex usage.
|
|
196
|
+
exclude_modules = [click, rx, typer, typing_extensions]
|
|
197
|
+
exclude_roots = [
|
|
198
|
+
p.parent.resolve()
|
|
199
|
+
if (p := Path(m.__file__)).name == "__init__.py" # pyright: ignore [reportArgumentType]
|
|
200
|
+
else p.resolve()
|
|
201
|
+
for m in exclude_modules
|
|
202
|
+
]
|
|
203
|
+
# Specifically exclude the reflex cli module.
|
|
204
|
+
if reflex_bin := shutil.which(b"reflex"):
|
|
205
|
+
exclude_roots.append(Path(reflex_bin.decode()))
|
|
206
|
+
|
|
207
|
+
frame = inspect.currentframe()
|
|
208
|
+
while frame := frame and frame.f_back:
|
|
209
|
+
frame_path = Path(inspect.getfile(frame)).resolve()
|
|
210
|
+
if not any(frame_path.is_relative_to(root) for root in exclude_roots):
|
|
211
|
+
break
|
|
212
|
+
return frame
|
|
213
|
+
|
|
214
|
+
|
|
196
215
|
def deprecate(
|
|
197
216
|
feature_name: str,
|
|
198
217
|
reason: str,
|
|
@@ -211,15 +230,27 @@ def deprecate(
|
|
|
211
230
|
dedupe: If True, suppress multiple console logs of deprecation message.
|
|
212
231
|
kwargs: Keyword arguments to pass to the print function.
|
|
213
232
|
"""
|
|
214
|
-
|
|
233
|
+
dedupe_key = feature_name
|
|
234
|
+
loc = ""
|
|
235
|
+
|
|
236
|
+
# See if we can find where the deprecation exists in "user code"
|
|
237
|
+
origin_frame = _get_first_non_framework_frame()
|
|
238
|
+
if origin_frame is not None:
|
|
239
|
+
filename = Path(origin_frame.f_code.co_filename)
|
|
240
|
+
if filename.is_relative_to(Path.cwd()):
|
|
241
|
+
filename = filename.relative_to(Path.cwd())
|
|
242
|
+
loc = f"{filename}:{origin_frame.f_lineno}"
|
|
243
|
+
dedupe_key = f"{dedupe_key} {loc}"
|
|
244
|
+
|
|
245
|
+
if dedupe_key not in _EMITTED_DEPRECATION_WARNINGS:
|
|
215
246
|
msg = (
|
|
216
247
|
f"{feature_name} has been deprecated in version {deprecation_version} {reason.rstrip('.')}. It will be completely "
|
|
217
|
-
f"removed in {removal_version}"
|
|
248
|
+
f"removed in {removal_version}. ({loc})"
|
|
218
249
|
)
|
|
219
250
|
if _LOG_LEVEL <= LogLevel.WARNING:
|
|
220
251
|
print(f"[yellow]DeprecationWarning: {msg}[/yellow]", **kwargs)
|
|
221
252
|
if dedupe:
|
|
222
|
-
_EMITTED_DEPRECATION_WARNINGS.add(
|
|
253
|
+
_EMITTED_DEPRECATION_WARNINGS.add(dedupe_key)
|
|
223
254
|
|
|
224
255
|
|
|
225
256
|
def error(msg: str, dedupe: bool = False, **kwargs):
|
|
@@ -244,7 +275,7 @@ def ask(
|
|
|
244
275
|
choices: list[str] | None = None,
|
|
245
276
|
default: str | None = None,
|
|
246
277
|
show_choices: bool = True,
|
|
247
|
-
) -> str:
|
|
278
|
+
) -> str | None:
|
|
248
279
|
"""Takes a prompt question and optionally a list of choices
|
|
249
280
|
and returns the user input.
|
|
250
281
|
|
|
@@ -259,7 +290,7 @@ def ask(
|
|
|
259
290
|
"""
|
|
260
291
|
return Prompt.ask(
|
|
261
292
|
question, choices=choices, default=default, show_choices=show_choices
|
|
262
|
-
)
|
|
293
|
+
)
|
|
263
294
|
|
|
264
295
|
|
|
265
296
|
def progress():
|
reflex/utils/exceptions.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Custom Exceptions."""
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class ReflexError(Exception):
|
|
@@ -11,7 +11,7 @@ class ConfigError(ReflexError):
|
|
|
11
11
|
"""Custom exception for config related errors."""
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
class
|
|
14
|
+
class InvalidStateManagerModeError(ReflexError, ValueError):
|
|
15
15
|
"""Raised when an invalid state manager mode is provided."""
|
|
16
16
|
|
|
17
17
|
|
|
@@ -31,6 +31,22 @@ class ComponentTypeError(ReflexError, TypeError):
|
|
|
31
31
|
"""Custom TypeError for component related errors."""
|
|
32
32
|
|
|
33
33
|
|
|
34
|
+
class ChildrenTypeError(ComponentTypeError):
|
|
35
|
+
"""Raised when the children prop of a component is not a valid type."""
|
|
36
|
+
|
|
37
|
+
def __init__(self, component: str, child: Any):
|
|
38
|
+
"""Initialize the exception.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
component: The name of the component.
|
|
42
|
+
child: The child that caused the error.
|
|
43
|
+
"""
|
|
44
|
+
super().__init__(
|
|
45
|
+
f"Component {component} received child {child} of type {type(child)}. "
|
|
46
|
+
"Accepted types are other components, state vars, or primitive Python types (dict excluded)."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
34
50
|
class EventHandlerTypeError(ReflexError, TypeError):
|
|
35
51
|
"""Custom TypeError for event handler related errors."""
|
|
36
52
|
|
|
@@ -59,6 +75,34 @@ class VarAttributeError(ReflexError, AttributeError):
|
|
|
59
75
|
"""Custom AttributeError for var related errors."""
|
|
60
76
|
|
|
61
77
|
|
|
78
|
+
class UntypedVarError(ReflexError, TypeError):
|
|
79
|
+
"""Custom TypeError for untyped var errors."""
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class UntypedComputedVarError(ReflexError, TypeError):
|
|
83
|
+
"""Custom TypeError for untyped computed var errors."""
|
|
84
|
+
|
|
85
|
+
def __init__(self, var_name: str):
|
|
86
|
+
"""Initialize the UntypedComputedVarError.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
var_name: The name of the computed var.
|
|
90
|
+
"""
|
|
91
|
+
super().__init__(f"Computed var '{var_name}' must have a type annotation.")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class MissingAnnotationError(ReflexError, TypeError):
|
|
95
|
+
"""Custom TypeError for missing annotations."""
|
|
96
|
+
|
|
97
|
+
def __init__(self, var_name: str):
|
|
98
|
+
"""Initialize the MissingAnnotationError.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
var_name: The name of the var.
|
|
102
|
+
"""
|
|
103
|
+
super().__init__(f"Var '{var_name}' must have a type annotation.")
|
|
104
|
+
|
|
105
|
+
|
|
62
106
|
class UploadValueError(ReflexError, ValueError):
|
|
63
107
|
"""Custom ValueError for upload related errors."""
|
|
64
108
|
|
|
@@ -95,43 +139,43 @@ class MatchTypeError(ReflexError, TypeError):
|
|
|
95
139
|
"""Raised when the return types of match cases are different."""
|
|
96
140
|
|
|
97
141
|
|
|
98
|
-
class
|
|
142
|
+
class EventHandlerArgTypeMismatchError(ReflexError, TypeError):
|
|
99
143
|
"""Raised when the annotations of args accepted by an EventHandler differs from the spec of the event trigger."""
|
|
100
144
|
|
|
101
145
|
|
|
102
|
-
class
|
|
146
|
+
class EventFnArgMismatchError(ReflexError, TypeError):
|
|
103
147
|
"""Raised when the number of args required by an event handler is more than provided by the event trigger."""
|
|
104
148
|
|
|
105
149
|
|
|
106
|
-
class
|
|
150
|
+
class DynamicRouteArgShadowsStateVarError(ReflexError, NameError):
|
|
107
151
|
"""Raised when a dynamic route arg shadows a state var."""
|
|
108
152
|
|
|
109
153
|
|
|
110
|
-
class
|
|
154
|
+
class ComputedVarShadowsStateVarError(ReflexError, NameError):
|
|
111
155
|
"""Raised when a computed var shadows a state var."""
|
|
112
156
|
|
|
113
157
|
|
|
114
|
-
class
|
|
158
|
+
class ComputedVarShadowsBaseVarsError(ReflexError, NameError):
|
|
115
159
|
"""Raised when a computed var shadows a base var."""
|
|
116
160
|
|
|
117
161
|
|
|
118
|
-
class
|
|
162
|
+
class EventHandlerShadowsBuiltInStateMethodError(ReflexError, NameError):
|
|
119
163
|
"""Raised when an event handler shadows a built-in state method."""
|
|
120
164
|
|
|
121
165
|
|
|
122
|
-
class
|
|
166
|
+
class GeneratedCodeHasNoFunctionDefsError(ReflexError):
|
|
123
167
|
"""Raised when refactored code generated with flexgen has no functions defined."""
|
|
124
168
|
|
|
125
169
|
|
|
126
|
-
class
|
|
170
|
+
class PrimitiveUnserializableToJSONError(ReflexError, ValueError):
|
|
127
171
|
"""Raised when a primitive type is unserializable to JSON. Usually with NaN and Infinity."""
|
|
128
172
|
|
|
129
173
|
|
|
130
|
-
class
|
|
174
|
+
class InvalidLifespanTaskTypeError(ReflexError, TypeError):
|
|
131
175
|
"""Raised when an invalid task type is registered as a lifespan task."""
|
|
132
176
|
|
|
133
177
|
|
|
134
|
-
class
|
|
178
|
+
class DynamicComponentMissingLibraryError(ReflexError, ValueError):
|
|
135
179
|
"""Raised when a dynamic component is missing a library."""
|
|
136
180
|
|
|
137
181
|
|
|
@@ -147,7 +191,7 @@ class EnvironmentVarValueError(ReflexError, ValueError):
|
|
|
147
191
|
"""Raised when an environment variable is set to an invalid value."""
|
|
148
192
|
|
|
149
193
|
|
|
150
|
-
class
|
|
194
|
+
class DynamicComponentInvalidSignatureError(ReflexError, TypeError):
|
|
151
195
|
"""Raised when a dynamic component has an invalid signature."""
|
|
152
196
|
|
|
153
197
|
|
|
@@ -163,26 +207,32 @@ class StateSerializationError(ReflexError):
|
|
|
163
207
|
"""Raised when the state cannot be serialized."""
|
|
164
208
|
|
|
165
209
|
|
|
210
|
+
class StateMismatchError(ReflexError, ValueError):
|
|
211
|
+
"""Raised when the state retrieved does not match the expected state."""
|
|
212
|
+
|
|
213
|
+
|
|
166
214
|
class SystemPackageMissingError(ReflexError):
|
|
167
215
|
"""Raised when a system package is missing."""
|
|
168
216
|
|
|
217
|
+
def __init__(self, package: str):
|
|
218
|
+
"""Initialize the SystemPackageMissingError.
|
|
169
219
|
|
|
170
|
-
|
|
171
|
-
|
|
220
|
+
Args:
|
|
221
|
+
package: The missing package.
|
|
222
|
+
"""
|
|
223
|
+
from reflex.constants import IS_MACOS
|
|
172
224
|
|
|
173
|
-
|
|
174
|
-
|
|
225
|
+
extra = (
|
|
226
|
+
f" You can do so by running 'brew install {package}'." if IS_MACOS else ""
|
|
227
|
+
)
|
|
228
|
+
super().__init__(
|
|
229
|
+
f"System package '{package}' is missing."
|
|
230
|
+
f" Please install it through your system package manager.{extra}"
|
|
231
|
+
)
|
|
175
232
|
|
|
176
|
-
Raises:
|
|
177
|
-
SystemPackageMissingError: The raised exception.
|
|
178
|
-
"""
|
|
179
|
-
from reflex.constants import IS_MACOS
|
|
180
233
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
" Please install it through your system package manager."
|
|
184
|
-
+ (f" You can do so by running 'brew install {package}'." if IS_MACOS else "")
|
|
185
|
-
)
|
|
234
|
+
class EventDeserializationError(ReflexError, ValueError):
|
|
235
|
+
"""Raised when an event cannot be deserialized."""
|
|
186
236
|
|
|
187
237
|
|
|
188
238
|
class InvalidLockWarningThresholdError(ReflexError):
|