reflex 0.7.0a5__py3-none-any.whl → 0.7.1__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.

Files changed (127) hide show
  1. reflex/.templates/jinja/web/package.json.jinja2 +7 -1
  2. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -1
  3. reflex/__init__.py +1 -0
  4. reflex/__init__.pyi +1 -0
  5. reflex/app.py +268 -85
  6. reflex/base.py +4 -10
  7. reflex/compiler/compiler.py +46 -12
  8. reflex/compiler/templates.py +1 -2
  9. reflex/compiler/utils.py +23 -14
  10. reflex/components/base/bare.py +109 -16
  11. reflex/components/component.py +179 -124
  12. reflex/components/core/__init__.py +1 -0
  13. reflex/components/core/__init__.pyi +1 -0
  14. reflex/components/core/auto_scroll.py +114 -0
  15. reflex/components/core/auto_scroll.pyi +284 -0
  16. reflex/components/core/banner.py +40 -9
  17. reflex/components/core/banner.pyi +400 -87
  18. reflex/components/core/breakpoints.py +1 -1
  19. reflex/components/core/cond.py +0 -8
  20. reflex/components/core/foreach.py +12 -2
  21. reflex/components/core/html.pyi +200 -19
  22. reflex/components/core/match.py +4 -4
  23. reflex/components/core/sticky.pyi +874 -90
  24. reflex/components/core/upload.py +3 -5
  25. reflex/components/core/upload.pyi +2 -4
  26. reflex/components/datadisplay/code.py +36 -10
  27. reflex/components/datadisplay/code.pyi +1 -1
  28. reflex/components/datadisplay/dataeditor.py +1 -3
  29. reflex/components/datadisplay/dataeditor.pyi +1 -3
  30. reflex/components/el/elements/base.py +95 -17
  31. reflex/components/el/elements/base.pyi +278 -19
  32. reflex/components/el/elements/forms.py +124 -102
  33. reflex/components/el/elements/forms.pyi +2787 -365
  34. reflex/components/el/elements/inline.py +24 -15
  35. reflex/components/el/elements/inline.pyi +5655 -546
  36. reflex/components/el/elements/media.py +79 -95
  37. reflex/components/el/elements/media.pyi +5167 -565
  38. reflex/components/el/elements/metadata.py +19 -17
  39. reflex/components/el/elements/metadata.pyi +841 -89
  40. reflex/components/el/elements/other.py +3 -5
  41. reflex/components/el/elements/other.pyi +1404 -137
  42. reflex/components/el/elements/scripts.py +10 -13
  43. reflex/components/el/elements/scripts.pyi +634 -65
  44. reflex/components/el/elements/sectioning.pyi +3001 -286
  45. reflex/components/el/elements/tables.py +14 -35
  46. reflex/components/el/elements/tables.pyi +2029 -218
  47. reflex/components/el/elements/typography.py +10 -13
  48. reflex/components/el/elements/typography.pyi +3014 -297
  49. reflex/components/lucide/icon.py +22 -6
  50. reflex/components/markdown/markdown.py +30 -10
  51. reflex/components/markdown/markdown.pyi +3 -2
  52. reflex/components/plotly/plotly.py +1 -3
  53. reflex/components/plotly/plotly.pyi +1 -3
  54. reflex/components/radix/primitives/form.pyi +624 -93
  55. reflex/components/radix/themes/color_mode.py +1 -1
  56. reflex/components/radix/themes/color_mode.pyi +213 -31
  57. reflex/components/radix/themes/components/alert_dialog.pyi +199 -18
  58. reflex/components/radix/themes/components/badge.pyi +199 -18
  59. reflex/components/radix/themes/components/button.pyi +213 -31
  60. reflex/components/radix/themes/components/callout.pyi +1000 -95
  61. reflex/components/radix/themes/components/card.pyi +199 -18
  62. reflex/components/radix/themes/components/context_menu.py +79 -1
  63. reflex/components/radix/themes/components/context_menu.pyi +320 -1
  64. reflex/components/radix/themes/components/dialog.pyi +199 -18
  65. reflex/components/radix/themes/components/hover_card.pyi +199 -18
  66. reflex/components/radix/themes/components/icon_button.pyi +213 -31
  67. reflex/components/radix/themes/components/inset.pyi +199 -18
  68. reflex/components/radix/themes/components/popover.pyi +199 -18
  69. reflex/components/radix/themes/components/table.pyi +1437 -154
  70. reflex/components/radix/themes/components/text_area.py +2 -2
  71. reflex/components/radix/themes/components/text_area.pyi +201 -20
  72. reflex/components/radix/themes/components/text_field.py +1 -1
  73. reflex/components/radix/themes/components/text_field.pyi +444 -88
  74. reflex/components/radix/themes/layout/box.pyi +200 -19
  75. reflex/components/radix/themes/layout/center.pyi +199 -18
  76. reflex/components/radix/themes/layout/container.pyi +199 -18
  77. reflex/components/radix/themes/layout/flex.pyi +199 -18
  78. reflex/components/radix/themes/layout/grid.pyi +199 -18
  79. reflex/components/radix/themes/layout/list.pyi +604 -57
  80. reflex/components/radix/themes/layout/section.pyi +199 -18
  81. reflex/components/radix/themes/layout/spacer.pyi +199 -18
  82. reflex/components/radix/themes/layout/stack.pyi +597 -54
  83. reflex/components/radix/themes/typography/blockquote.pyi +200 -19
  84. reflex/components/radix/themes/typography/code.pyi +199 -18
  85. reflex/components/radix/themes/typography/heading.pyi +199 -18
  86. reflex/components/radix/themes/typography/link.pyi +238 -28
  87. reflex/components/radix/themes/typography/text.pyi +1394 -127
  88. reflex/components/react_player/react_player.py +1 -1
  89. reflex/components/react_player/react_player.pyi +1 -3
  90. reflex/components/sonner/toast.py +41 -12
  91. reflex/components/sonner/toast.pyi +20 -6
  92. reflex/components/tags/iter_tag.py +4 -0
  93. reflex/components/tags/tag.py +3 -3
  94. reflex/config.py +187 -28
  95. reflex/constants/__init__.py +2 -0
  96. reflex/constants/base.py +6 -0
  97. reflex/constants/compiler.py +9 -0
  98. reflex/constants/event.py +1 -0
  99. reflex/constants/installer.py +8 -5
  100. reflex/constants/utils.py +1 -3
  101. reflex/event.py +7 -16
  102. reflex/experimental/layout.pyi +597 -54
  103. reflex/py.typed +0 -0
  104. reflex/reflex.py +30 -41
  105. reflex/state.py +49 -44
  106. reflex/style.py +15 -22
  107. reflex/testing.py +2 -0
  108. reflex/utils/build.py +12 -0
  109. reflex/utils/console.py +4 -0
  110. reflex/utils/decorator.py +25 -0
  111. reflex/utils/exec.py +92 -34
  112. reflex/utils/format.py +35 -6
  113. reflex/utils/path_ops.py +16 -1
  114. reflex/utils/prerequisites.py +25 -33
  115. reflex/utils/processes.py +12 -13
  116. reflex/utils/serializers.py +20 -43
  117. reflex/utils/telemetry.py +4 -15
  118. reflex/utils/types.py +36 -66
  119. reflex/vars/base.py +53 -76
  120. reflex/vars/function.py +17 -5
  121. reflex/vars/number.py +1 -1
  122. reflex/vars/sequence.py +80 -4
  123. {reflex-0.7.0a5.dist-info → reflex-0.7.1.dist-info}/METADATA +4 -5
  124. {reflex-0.7.0a5.dist-info → reflex-0.7.1.dist-info}/RECORD +127 -123
  125. {reflex-0.7.0a5.dist-info → reflex-0.7.1.dist-info}/LICENSE +0 -0
  126. {reflex-0.7.0a5.dist-info → reflex-0.7.1.dist-info}/WHEEL +0 -0
  127. {reflex-0.7.0a5.dist-info → reflex-0.7.1.dist-info}/entry_points.txt +0 -0
reflex/py.typed ADDED
File without changes
reflex/reflex.py CHANGED
@@ -20,13 +20,8 @@ from reflex.utils import console, telemetry
20
20
  typer.core.rich = None # pyright: ignore [reportPrivateImportUsage]
21
21
 
22
22
  # Create the app.
23
- try:
24
- cli = typer.Typer(add_completion=False, pretty_exceptions_enable=False)
25
- except TypeError:
26
- # Fallback for older typer versions.
27
- cli = typer.Typer(add_completion=False)
23
+ cli = typer.Typer(add_completion=False, pretty_exceptions_enable=False)
28
24
 
29
- SHOW_BUILT_WITH_REFLEX_INFO = "https://reflex.dev/docs/hosting/reflex-branding/"
30
25
 
31
26
  # Get the config.
32
27
  config = get_config()
@@ -127,8 +122,8 @@ def _run(
127
122
  env: constants.Env = constants.Env.DEV,
128
123
  frontend: bool = True,
129
124
  backend: bool = True,
130
- frontend_port: int = config.frontend_port,
131
- backend_port: int = config.backend_port,
125
+ frontend_port: int | None = None,
126
+ backend_port: int | None = None,
132
127
  backend_host: str = config.backend_host,
133
128
  loglevel: constants.LogLevel = config.loglevel,
134
129
  ):
@@ -158,17 +153,28 @@ def _run(
158
153
 
159
154
  # Find the next available open port if applicable.
160
155
  if frontend:
156
+ auto_increment_frontend = not bool(frontend_port or config.frontend_port)
161
157
  frontend_port = processes.handle_port(
162
158
  "frontend",
163
- frontend_port,
164
- constants.DefaultPorts.FRONTEND_PORT,
159
+ (
160
+ frontend_port
161
+ or config.frontend_port
162
+ or constants.DefaultPorts.FRONTEND_PORT
163
+ ),
164
+ auto_increment=auto_increment_frontend,
165
165
  )
166
166
 
167
167
  if backend:
168
+ auto_increment_backend = not bool(backend_port or config.backend_port)
169
+
168
170
  backend_port = processes.handle_port(
169
171
  "backend",
170
- backend_port,
171
- constants.DefaultPorts.BACKEND_PORT,
172
+ (
173
+ backend_port
174
+ or config.backend_port
175
+ or constants.DefaultPorts.BACKEND_PORT
176
+ ),
177
+ auto_increment=auto_increment_backend,
172
178
  )
173
179
 
174
180
  # Apply the new ports to the config.
@@ -185,15 +191,6 @@ def _run(
185
191
  prerequisites.check_latest_package_version(constants.Reflex.MODULE_NAME)
186
192
 
187
193
  if frontend:
188
- if not config.show_built_with_reflex:
189
- # The sticky badge may be disabled at runtime for team/enterprise tiers.
190
- prerequisites.check_config_option_in_tier(
191
- option_name="show_built_with_reflex",
192
- allowed_tiers=["team", "enterprise"],
193
- fallback_value=True,
194
- help_link=SHOW_BUILT_WITH_REFLEX_INFO,
195
- )
196
-
197
194
  # Get the app module.
198
195
  prerequisites.get_compiled_app()
199
196
 
@@ -246,7 +243,7 @@ def _run(
246
243
  # Start the frontend and backend.
247
244
  with processes.run_concurrently_context(*commands):
248
245
  # In dev mode, run the backend on the main thread.
249
- if backend and env == constants.Env.DEV:
246
+ if backend and backend_port and env == constants.Env.DEV:
250
247
  backend_cmd(
251
248
  backend_host, int(backend_port), loglevel.subprocess_level(), frontend
252
249
  )
@@ -275,10 +272,14 @@ def run(
275
272
  envvar=environment.REFLEX_BACKEND_ONLY.name,
276
273
  ),
277
274
  frontend_port: int = typer.Option(
278
- config.frontend_port, help="Specify a different frontend port."
275
+ config.frontend_port,
276
+ help="Specify a different frontend port.",
277
+ envvar=environment.REFLEX_FRONTEND_PORT.name,
279
278
  ),
280
279
  backend_port: int = typer.Option(
281
- config.backend_port, help="Specify a different backend port."
280
+ config.backend_port,
281
+ help="Specify a different backend port.",
282
+ envvar=environment.REFLEX_BACKEND_PORT.name,
282
283
  ),
283
284
  backend_host: str = typer.Option(
284
285
  config.backend_host, help="Specify the backend host."
@@ -291,6 +292,8 @@ def run(
291
292
  if frontend and backend:
292
293
  console.error("Cannot use both --frontend-only and --backend-only options.")
293
294
  raise typer.Exit(1)
295
+
296
+ environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.RUN)
294
297
  environment.REFLEX_BACKEND_ONLY.set(backend)
295
298
  environment.REFLEX_FRONTEND_ONLY.set(frontend)
296
299
 
@@ -337,20 +340,13 @@ def export(
337
340
  from reflex.utils import export as export_utils
338
341
  from reflex.utils import prerequisites
339
342
 
343
+ environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.EXPORT)
344
+
340
345
  frontend, backend = prerequisites.check_running_mode(frontend, backend)
341
346
 
342
347
  if prerequisites.needs_reinit(frontend=frontend or not backend):
343
348
  _init(name=config.app_name, loglevel=loglevel)
344
349
 
345
- if frontend and not config.show_built_with_reflex:
346
- # The sticky badge may be disabled on export for team/enterprise tiers.
347
- prerequisites.check_config_option_in_tier(
348
- option_name="show_built_with_reflex",
349
- allowed_tiers=["team", "enterprise"],
350
- fallback_value=False,
351
- help_link=SHOW_BUILT_WITH_REFLEX_INFO,
352
- )
353
-
354
350
  export_utils.export(
355
351
  zipping=zipping,
356
352
  frontend=frontend,
@@ -545,14 +541,7 @@ def deploy(
545
541
 
546
542
  check_version()
547
543
 
548
- if not config.show_built_with_reflex:
549
- # The sticky badge may be disabled on deploy for pro/team/enterprise tiers.
550
- prerequisites.check_config_option_in_tier(
551
- option_name="show_built_with_reflex",
552
- allowed_tiers=["pro", "team", "enterprise"],
553
- fallback_value=True,
554
- help_link=SHOW_BUILT_WITH_REFLEX_INFO,
555
- )
544
+ environment.REFLEX_COMPILE_CONTEXT.set(constants.CompileContext.DEPLOY)
556
545
 
557
546
  # Set the log level.
558
547
  console.set_log_level(loglevel)
reflex/state.py CHANGED
@@ -40,50 +40,22 @@ from typing import (
40
40
  get_type_hints,
41
41
  )
42
42
 
43
- from redis.asyncio.client import PubSub
44
- from sqlalchemy.orm import DeclarativeBase
45
- from typing_extensions import Self
46
-
47
- from reflex import event
48
- from reflex.config import PerformanceMode, get_config
49
- from reflex.istate.data import RouterData
50
- from reflex.istate.storage import ClientStorageBase
51
- from reflex.model import Model
52
- from reflex.vars.base import (
53
- ComputedVar,
54
- DynamicRouteVar,
55
- Var,
56
- computed_var,
57
- dispatch,
58
- get_unique_variable_name,
59
- is_computed_var,
60
- )
61
-
62
- try:
63
- import pydantic.v1 as pydantic
64
- except ModuleNotFoundError:
65
- import pydantic
66
-
67
- from pydantic import BaseModel as BaseModelV2
68
-
69
- try:
70
- from pydantic.v1 import BaseModel as BaseModelV1
71
- except ModuleNotFoundError:
72
- BaseModelV1 = BaseModelV2
73
-
74
- try:
75
- from pydantic.v1 import validator
76
- except ModuleNotFoundError:
77
- from pydantic import validator
78
-
43
+ import pydantic.v1 as pydantic
79
44
  import wrapt
45
+ from pydantic import BaseModel as BaseModelV2
46
+ from pydantic.v1 import BaseModel as BaseModelV1
47
+ from pydantic.v1 import validator
48
+ from pydantic.v1.fields import ModelField
80
49
  from redis.asyncio import Redis
50
+ from redis.asyncio.client import PubSub
81
51
  from redis.exceptions import ResponseError
52
+ from sqlalchemy.orm import DeclarativeBase
53
+ from typing_extensions import Self
82
54
 
83
55
  import reflex.istate.dynamic
84
- from reflex import constants
56
+ from reflex import constants, event
85
57
  from reflex.base import Base
86
- from reflex.config import environment
58
+ from reflex.config import PerformanceMode, environment, get_config
87
59
  from reflex.event import (
88
60
  BACKGROUND_TASK_MARKER,
89
61
  Event,
@@ -91,6 +63,9 @@ from reflex.event import (
91
63
  EventSpec,
92
64
  fix_events,
93
65
  )
66
+ from reflex.istate.data import RouterData
67
+ from reflex.istate.storage import ClientStorageBase
68
+ from reflex.model import Model
94
69
  from reflex.utils import console, format, path_ops, prerequisites, types
95
70
  from reflex.utils.exceptions import (
96
71
  ComputedVarShadowsBaseVarsError,
@@ -121,6 +96,15 @@ from reflex.utils.types import (
121
96
  value_inside_optional,
122
97
  )
123
98
  from reflex.vars import VarData
99
+ from reflex.vars.base import (
100
+ ComputedVar,
101
+ DynamicRouteVar,
102
+ Var,
103
+ computed_var,
104
+ dispatch,
105
+ get_unique_variable_name,
106
+ is_computed_var,
107
+ )
124
108
 
125
109
  if TYPE_CHECKING:
126
110
  from reflex.components.component import Component
@@ -289,10 +273,6 @@ class EventHandlerSetVar(EventHandler):
289
273
  return super().__call__(*args)
290
274
 
291
275
 
292
- if TYPE_CHECKING:
293
- from pydantic.v1.fields import ModelField
294
-
295
-
296
276
  def _unwrap_field_type(type_: Type) -> Type:
297
277
  """Unwrap rx.Field type annotations.
298
278
 
@@ -347,6 +327,9 @@ async def _resolve_delta(delta: Delta) -> Delta:
347
327
  return delta
348
328
 
349
329
 
330
+ all_base_state_classes: dict[str, None] = {}
331
+
332
+
350
333
  class BaseState(Base, ABC, extra=pydantic.Extra.allow):
351
334
  """The state of the app."""
352
335
 
@@ -644,6 +627,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
644
627
  cls._var_dependencies = {}
645
628
  cls._init_var_dependency_dicts()
646
629
 
630
+ all_base_state_classes[cls.get_full_name()] = None
631
+
647
632
  @staticmethod
648
633
  def _copy_fn(fn: Callable) -> Callable:
649
634
  """Copy a function. Used to copy ComputedVars and EventHandlers from mixins.
@@ -1742,6 +1727,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1742
1727
 
1743
1728
  Yields:
1744
1729
  StateUpdate object
1730
+
1731
+ Raises:
1732
+ ValueError: If a string value is received for an int or float type and cannot be converted.
1745
1733
  """
1746
1734
  from reflex.utils import telemetry
1747
1735
 
@@ -1779,12 +1767,25 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
1779
1767
  hinted_args, (Base, BaseModelV1, BaseModelV2)
1780
1768
  ):
1781
1769
  payload[arg] = hinted_args(**value)
1782
- if isinstance(value, list) and (hinted_args is set or hinted_args is Set):
1770
+ elif isinstance(value, list) and (hinted_args is set or hinted_args is Set):
1783
1771
  payload[arg] = set(value)
1784
- if isinstance(value, list) and (
1772
+ elif isinstance(value, list) and (
1785
1773
  hinted_args is tuple or hinted_args is Tuple
1786
1774
  ):
1787
1775
  payload[arg] = tuple(value)
1776
+ elif isinstance(value, str) and (
1777
+ hinted_args is int or hinted_args is float
1778
+ ):
1779
+ try:
1780
+ payload[arg] = hinted_args(value)
1781
+ except ValueError:
1782
+ raise ValueError(
1783
+ f"Received a string value ({value}) for {arg} but expected a {hinted_args}"
1784
+ ) from None
1785
+ else:
1786
+ console.warn(
1787
+ f"Received a string value ({value}) for {arg} but expected a {hinted_args}. A simple conversion was successful."
1788
+ )
1788
1789
 
1789
1790
  # Wrap the function in a try/except block.
1790
1791
  try:
@@ -2459,6 +2460,8 @@ class ComponentState(State, mixin=True):
2459
2460
  Returns:
2460
2461
  A new instance of the Component with an independent copy of the State.
2461
2462
  """
2463
+ from reflex.compiler.compiler import into_component
2464
+
2462
2465
  cls._per_component_state_instance_count += 1
2463
2466
  state_cls_name = f"{cls.__name__}_n{cls._per_component_state_instance_count}"
2464
2467
  component_state = type(
@@ -2470,6 +2473,7 @@ class ComponentState(State, mixin=True):
2470
2473
  # Save a reference to the dynamic state for pickle/unpickle.
2471
2474
  setattr(reflex.istate.dynamic, state_cls_name, component_state)
2472
2475
  component = component_state.get_component(*children, **props)
2476
+ component = into_component(component)
2473
2477
  component.State = component_state
2474
2478
  return component
2475
2479
 
@@ -4088,6 +4092,7 @@ def reload_state_module(
4088
4092
  for subclass in tuple(state.class_subclasses):
4089
4093
  reload_state_module(module=module, state=subclass)
4090
4094
  if subclass.__module__ == module and module is not None:
4095
+ all_base_state_classes.pop(subclass.get_full_name(), None)
4091
4096
  state.class_subclasses.remove(subclass)
4092
4097
  state._always_dirty_substates.discard(subclass.get_name())
4093
4098
  state._var_dependencies = {}
reflex/style.py CHANGED
@@ -6,13 +6,13 @@ from typing import Any, Literal, Tuple, Type
6
6
 
7
7
  from reflex import constants
8
8
  from reflex.components.core.breakpoints import Breakpoints, breakpoints_values
9
- from reflex.event import EventChain, EventHandler
9
+ from reflex.event import EventChain, EventHandler, EventSpec, run_script
10
10
  from reflex.utils import format
11
11
  from reflex.utils.exceptions import ReflexError
12
12
  from reflex.utils.imports import ImportVar
13
13
  from reflex.utils.types import get_origin
14
14
  from reflex.vars import VarData
15
- from reflex.vars.base import CallableVar, LiteralVar, Var
15
+ from reflex.vars.base import LiteralVar, Var
16
16
  from reflex.vars.function import FunctionVar
17
17
  from reflex.vars.object import ObjectVar
18
18
 
@@ -48,11 +48,10 @@ def _color_mode_var(_js_expr: str, _var_type: Type = str) -> Var:
48
48
  ).guess_type()
49
49
 
50
50
 
51
- @CallableVar
52
51
  def set_color_mode(
53
- new_color_mode: LiteralColorMode | Var[LiteralColorMode] | None = None,
54
- ) -> Var[EventChain]:
55
- """Create an EventChain Var that sets the color mode to a specific value.
52
+ new_color_mode: LiteralColorMode | Var[LiteralColorMode],
53
+ ) -> EventSpec:
54
+ """Create an EventSpec Var that sets the color mode to a specific value.
56
55
 
57
56
  Note: `set_color_mode` is not a real event and cannot be triggered from a
58
57
  backend event handler.
@@ -61,24 +60,15 @@ def set_color_mode(
61
60
  new_color_mode: The color mode to set.
62
61
 
63
62
  Returns:
64
- The EventChain Var that can be passed to an event trigger.
63
+ The EventSpec Var that can be passed to an event trigger.
65
64
  """
66
65
  base_setter = _color_mode_var(
67
66
  _js_expr=constants.ColorMode.SET,
68
- _var_type=EventChain,
69
- )
70
- if new_color_mode is None:
71
- return base_setter
72
-
73
- if not isinstance(new_color_mode, Var):
74
- new_color_mode = LiteralVar.create(new_color_mode)
67
+ ).to(FunctionVar)
75
68
 
76
- return Var(
77
- f"() => {base_setter!s}({new_color_mode!s})",
78
- _var_data=VarData.merge(
79
- base_setter._get_all_var_data(), new_color_mode._get_all_var_data()
80
- ),
81
- ).to(FunctionVar, EventChain)
69
+ return run_script(
70
+ base_setter.call(new_color_mode),
71
+ )
82
72
 
83
73
 
84
74
  # Var resolves to the current color mode for the app ("light", "dark" or "system")
@@ -191,11 +181,12 @@ def convert(
191
181
  for key, value in style_dict.items():
192
182
  keys = (
193
183
  format_style_key(key)
194
- if not isinstance(value, (dict, ObjectVar))
184
+ if not isinstance(value, (dict, ObjectVar, list))
195
185
  or (
196
186
  isinstance(value, Breakpoints)
197
187
  and all(not isinstance(v, dict) for v in value.values())
198
188
  )
189
+ or (isinstance(value, list) and all(not isinstance(v, dict) for v in value))
199
190
  or (
200
191
  isinstance(value, ObjectVar)
201
192
  and not issubclass(get_origin(value._var_type) or value._var_type, dict)
@@ -237,7 +228,9 @@ def format_style_key(key: str) -> Tuple[str, ...]:
237
228
  Returns:
238
229
  Tuple of css style names corresponding to the key provided.
239
230
  """
240
- key = format.to_camel_case(key, allow_hyphens=True)
231
+ if key.startswith("--"):
232
+ return (key,)
233
+ key = format.to_camel_case(key)
241
234
  return STYLE_PROP_SHORTHAND_MAPPING.get(key, (key,))
242
235
 
243
236
 
reflex/testing.py CHANGED
@@ -43,6 +43,7 @@ import reflex.utils.exec
43
43
  import reflex.utils.format
44
44
  import reflex.utils.prerequisites
45
45
  import reflex.utils.processes
46
+ from reflex.components.component import CustomComponent
46
47
  from reflex.config import environment
47
48
  from reflex.state import (
48
49
  BaseState,
@@ -254,6 +255,7 @@ class AppHarness:
254
255
  # disable telemetry reporting for tests
255
256
 
256
257
  os.environ["TELEMETRY_ENABLED"] = "false"
258
+ CustomComponent.create().get_component.cache_clear()
257
259
  self.app_path.mkdir(parents=True, exist_ok=True)
258
260
  if self.app_source is not None:
259
261
  app_globals = self._get_globals_from_signature(self.app_source)
reflex/utils/build.py CHANGED
@@ -60,6 +60,7 @@ def _zip(
60
60
  dirs_to_exclude: set[str] | None = None,
61
61
  files_to_exclude: set[str] | None = None,
62
62
  top_level_dirs_to_exclude: set[str] | None = None,
63
+ globs_to_include: list[str] | None = None,
63
64
  ) -> None:
64
65
  """Zip utility function.
65
66
 
@@ -72,6 +73,7 @@ def _zip(
72
73
  dirs_to_exclude: The directories to exclude.
73
74
  files_to_exclude: The files to exclude.
74
75
  top_level_dirs_to_exclude: The top level directory names immediately under root_dir to exclude. Do not exclude folders by these names further in the sub-directories.
76
+ globs_to_include: Apply these globs from the root_dir and always include them in the zip.
75
77
 
76
78
  """
77
79
  target = Path(target)
@@ -103,6 +105,13 @@ def _zip(
103
105
  files_to_zip += [
104
106
  str(root / file) for file in files if file not in files_to_exclude
105
107
  ]
108
+ if globs_to_include:
109
+ for glob in globs_to_include:
110
+ files_to_zip += [
111
+ str(file)
112
+ for file in root_dir.glob(glob)
113
+ if file.name not in files_to_exclude
114
+ ]
106
115
 
107
116
  # Create a progress bar for zipping the component.
108
117
  progress = Progress(
@@ -160,6 +169,9 @@ def zip_app(
160
169
  top_level_dirs_to_exclude={"assets"},
161
170
  exclude_venv_dirs=True,
162
171
  upload_db_file=upload_db_file,
172
+ globs_to_include=[
173
+ str(Path(constants.Dirs.WEB) / constants.Dirs.BACKEND / "*")
174
+ ],
163
175
  )
164
176
 
165
177
 
reflex/utils/console.py CHANGED
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  import contextlib
6
6
  import inspect
7
+ import os
7
8
  import shutil
8
9
  import time
9
10
  from pathlib import Path
@@ -60,6 +61,9 @@ def set_log_level(log_level: LogLevel):
60
61
  f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
61
62
  )
62
63
  global _LOG_LEVEL
64
+ if log_level != _LOG_LEVEL:
65
+ # Set the loglevel persistenly for subprocesses.
66
+ os.environ["LOGLEVEL"] = log_level.value
63
67
  _LOG_LEVEL = log_level
64
68
 
65
69
 
@@ -0,0 +1,25 @@
1
+ """Decorator utilities."""
2
+
3
+ from typing import Callable, TypeVar
4
+
5
+ T = TypeVar("T")
6
+
7
+
8
+ def once(f: Callable[[], T]) -> Callable[[], T]:
9
+ """A decorator that calls the function once and caches the result.
10
+
11
+ Args:
12
+ f: The function to call.
13
+
14
+ Returns:
15
+ A function that calls the function once and caches the result.
16
+ """
17
+ unset = object()
18
+ value: object | T = unset
19
+
20
+ def wrapper() -> T:
21
+ nonlocal value
22
+ value = f() if value is unset else value
23
+ return value # pyright: ignore[reportReturnType]
24
+
25
+ return wrapper