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
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing_extensions import TypedDict
5
+ from typing import TypedDict
6
6
 
7
7
  from reflex.components.component import NoSSRComponent
8
8
  from reflex.event import EventHandler, no_args_event_spec, passthrough_event_spec
@@ -3,9 +3,7 @@
3
3
  # ------------------- DO NOT EDIT ----------------------
4
4
  # This file was generated by `reflex/utils/pyi_generator.py`!
5
5
  # ------------------------------------------------------
6
- from typing import Any, Dict, Optional, Union, overload
7
-
8
- from typing_extensions import TypedDict
6
+ from typing import Any, Dict, Optional, TypedDict, Union, overload
9
7
 
10
8
  from reflex.components.component import NoSSRComponent
11
9
  from reflex.event import EventType
@@ -2,12 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, ClassVar, Literal, Optional, Union
5
+ from typing import Any, Literal, Optional, Union
6
6
 
7
7
  from reflex.base import Base
8
8
  from reflex.components.component import Component, ComponentNamespace
9
9
  from reflex.components.lucide.icon import Icon
10
10
  from reflex.components.props import NoExtrasAllowedProps, PropsBase
11
+ from reflex.constants.base import Dirs
11
12
  from reflex.event import EventSpec, run_script
12
13
  from reflex.style import Style, resolved_color_mode
13
14
  from reflex.utils import format
@@ -16,6 +17,7 @@ from reflex.utils.serializers import serializer
16
17
  from reflex.vars import VarData
17
18
  from reflex.vars.base import LiteralVar, Var
18
19
  from reflex.vars.function import FunctionVar
20
+ from reflex.vars.number import ternary_operation
19
21
  from reflex.vars.object import ObjectVar
20
22
 
21
23
  LiteralPosition = Literal[
@@ -27,7 +29,10 @@ LiteralPosition = Literal[
27
29
  "bottom-right",
28
30
  ]
29
31
 
30
- toast_ref = Var(_js_expr="refs['__toast']")
32
+ toast_ref = Var(
33
+ _js_expr="refs['__toast']",
34
+ _var_data=VarData(imports={f"$/{Dirs.STATE_PATH}": [ImportVar(tag="refs")]}),
35
+ )
31
36
 
32
37
 
33
38
  class ToastAction(Base):
@@ -213,9 +218,6 @@ class Toaster(Component):
213
218
  # Pauses toast timers when the page is hidden, e.g., when the tab is backgrounded, the browser is minimized, or the OS is locked.
214
219
  pause_when_page_is_hidden: Var[bool]
215
220
 
216
- # Marked True when any Toast component is created.
217
- is_used: ClassVar[bool] = False
218
-
219
221
  def add_hooks(self) -> list[Var | str]:
220
222
  """Add hooks for the toaster component.
221
223
 
@@ -237,13 +239,17 @@ class Toaster(Component):
237
239
 
238
240
  @staticmethod
239
241
  def send_toast(
240
- message: str | Var = "", level: str | None = None, **props
242
+ message: str | Var = "",
243
+ level: str | None = None,
244
+ fallback_to_alert: bool = False,
245
+ **props,
241
246
  ) -> EventSpec:
242
247
  """Send a toast message.
243
248
 
244
249
  Args:
245
250
  message: The message to display.
246
251
  level: The level of the toast.
252
+ fallback_to_alert: Whether to fallback to an alert if the toaster is not created.
247
253
  **props: The options for the toast.
248
254
 
249
255
  Raises:
@@ -252,11 +258,6 @@ class Toaster(Component):
252
258
  Returns:
253
259
  The toast event.
254
260
  """
255
- if not Toaster.is_used:
256
- raise ValueError(
257
- "Toaster component must be created before sending a toast. (use `rx.toast.provider()`)"
258
- )
259
-
260
261
  toast_command = (
261
262
  ObjectVar.__getattr__(toast_ref.to(dict), level) if level else toast_ref
262
263
  ).to(FunctionVar)
@@ -273,6 +274,21 @@ class Toaster(Component):
273
274
  else:
274
275
  toast = toast_command.call(message)
275
276
 
277
+ if fallback_to_alert:
278
+ toast = ternary_operation(
279
+ toast_ref.bool(),
280
+ toast,
281
+ FunctionVar("window.alert").call(
282
+ Var.create(
283
+ message
284
+ if isinstance(message, str) and message
285
+ else props.get("title", props.get("description", ""))
286
+ )
287
+ .to(str)
288
+ .replace("<br/>", "\n")
289
+ ),
290
+ )
291
+
276
292
  return run_script(toast)
277
293
 
278
294
  @staticmethod
@@ -327,6 +343,19 @@ class Toaster(Component):
327
343
  """
328
344
  return Toaster.send_toast(message, level="success", **kwargs)
329
345
 
346
+ @staticmethod
347
+ def toast_loading(message: str | Var = "", **kwargs: Any):
348
+ """Display a loading toast message.
349
+
350
+ Args:
351
+ message: The message to display.
352
+ **kwargs: Additional toast props.
353
+
354
+ Returns:
355
+ The toast event.
356
+ """
357
+ return Toaster.send_toast(message, level="loading", **kwargs)
358
+
330
359
  @staticmethod
331
360
  def toast_dismiss(id: Var | str | None = None):
332
361
  """Dismiss a toast.
@@ -362,7 +391,6 @@ class Toaster(Component):
362
391
  Returns:
363
392
  The toaster component.
364
393
  """
365
- cls.is_used = True
366
394
  return super().create(*children, **props)
367
395
 
368
396
 
@@ -378,6 +406,7 @@ class ToastNamespace(ComponentNamespace):
378
406
  warning = staticmethod(Toaster.toast_warning)
379
407
  error = staticmethod(Toaster.toast_error)
380
408
  success = staticmethod(Toaster.toast_success)
409
+ loading = staticmethod(Toaster.toast_loading)
381
410
  dismiss = staticmethod(Toaster.toast_dismiss)
382
411
  __call__ = staticmethod(Toaster.send_toast)
383
412
 
@@ -3,15 +3,18 @@
3
3
  # ------------------- DO NOT EDIT ----------------------
4
4
  # This file was generated by `reflex/utils/pyi_generator.py`!
5
5
  # ------------------------------------------------------
6
- from typing import Any, ClassVar, Dict, Literal, Optional, Union, overload
6
+ from typing import Any, Dict, Literal, Optional, Union, overload
7
7
 
8
8
  from reflex.base import Base
9
9
  from reflex.components.component import Component, ComponentNamespace
10
10
  from reflex.components.lucide.icon import Icon
11
11
  from reflex.components.props import NoExtrasAllowedProps, PropsBase
12
+ from reflex.constants.base import Dirs
12
13
  from reflex.event import EventSpec, EventType
13
14
  from reflex.style import Style
15
+ from reflex.utils.imports import ImportVar
14
16
  from reflex.utils.serializers import serializer
17
+ from reflex.vars import VarData
15
18
  from reflex.vars.base import Var
16
19
 
17
20
  LiteralPosition = Literal[
@@ -22,7 +25,10 @@ LiteralPosition = Literal[
22
25
  "bottom-center",
23
26
  "bottom-right",
24
27
  ]
25
- toast_ref = Var(_js_expr="refs['__toast']")
28
+ toast_ref = Var(
29
+ _js_expr="refs['__toast']",
30
+ _var_data=VarData(imports={f"$/{Dirs.STATE_PATH}": [ImportVar(tag="refs")]}),
31
+ )
26
32
 
27
33
  class ToastAction(Base):
28
34
  label: str
@@ -54,12 +60,13 @@ class ToastProps(PropsBase, NoExtrasAllowedProps):
54
60
  def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
55
61
 
56
62
  class Toaster(Component):
57
- is_used: ClassVar[bool] = False
58
-
59
63
  def add_hooks(self) -> list[Var | str]: ...
60
64
  @staticmethod
61
65
  def send_toast(
62
- message: str | Var = "", level: str | None = None, **props
66
+ message: str | Var = "",
67
+ level: str | None = None,
68
+ fallback_to_alert: bool = False,
69
+ **props,
63
70
  ) -> EventSpec: ...
64
71
  @staticmethod
65
72
  def toast_info(message: str | Var = "", **kwargs: Any): ...
@@ -70,6 +77,8 @@ class Toaster(Component):
70
77
  @staticmethod
71
78
  def toast_success(message: str | Var = "", **kwargs: Any): ...
72
79
  @staticmethod
80
+ def toast_loading(message: str | Var = "", **kwargs: Any): ...
81
+ @staticmethod
73
82
  def toast_dismiss(id: Var | str | None = None): ...
74
83
  @overload
75
84
  @classmethod
@@ -172,17 +181,22 @@ class ToastNamespace(ComponentNamespace):
172
181
  warning = staticmethod(Toaster.toast_warning)
173
182
  error = staticmethod(Toaster.toast_error)
174
183
  success = staticmethod(Toaster.toast_success)
184
+ loading = staticmethod(Toaster.toast_loading)
175
185
  dismiss = staticmethod(Toaster.toast_dismiss)
176
186
 
177
187
  @staticmethod
178
188
  def __call__(
179
- message: Union[str, Var] = "", level: Optional[str] = None, **props
189
+ message: Union[str, Var] = "",
190
+ level: Optional[str] = None,
191
+ fallback_to_alert: bool = False,
192
+ **props,
180
193
  ) -> "EventSpec":
181
194
  """Send a toast message.
182
195
 
183
196
  Args:
184
197
  message: The message to display.
185
198
  level: The level of the toast.
199
+ fallback_to_alert: Whether to fallback to an alert if the toaster is not created.
186
200
  **props: The options for the toast.
187
201
 
188
202
  Raises:
@@ -134,6 +134,10 @@ class IterTag(Tag):
134
134
  if isinstance(component, (Foreach, Cond)):
135
135
  component = Fragment.create(component)
136
136
 
137
+ # If the component is a tuple, unpack and wrap it in a fragment.
138
+ if isinstance(component, tuple):
139
+ component = Fragment.create(*component)
140
+
137
141
  # Set the component key.
138
142
  if component.key is None:
139
143
  component.key = index
@@ -3,7 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import dataclasses
6
- from typing import Any, Dict, List, Optional, Sequence, Union
6
+ from typing import Any, Dict, List, Mapping, Optional, Sequence
7
7
 
8
8
  from reflex.event import EventChain
9
9
  from reflex.utils import format, types
@@ -101,9 +101,9 @@ class Tag:
101
101
  """
102
102
  self.props.update(
103
103
  {
104
- format.to_camel_case(name, allow_hyphens=True): (
104
+ format.to_camel_case(name, treat_hyphens_as_underscores=False): (
105
105
  prop
106
- if types._isinstance(prop, Union[EventChain, dict])
106
+ if types._isinstance(prop, (EventChain, Mapping))
107
107
  else LiteralVar.create(prop)
108
108
  ) # rx.color is always a string
109
109
  for name, prop in kwargs.items()
reflex/config.py CHANGED
@@ -2,20 +2,26 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import concurrent.futures
5
6
  import dataclasses
6
7
  import enum
7
8
  import importlib
8
9
  import inspect
10
+ import multiprocessing
9
11
  import os
12
+ import platform
10
13
  import sys
11
14
  import threading
12
15
  import urllib.parse
16
+ from functools import lru_cache
13
17
  from importlib.util import find_spec
14
18
  from pathlib import Path
15
19
  from types import ModuleType
16
20
  from typing import (
17
21
  TYPE_CHECKING,
22
+ Annotated,
18
23
  Any,
24
+ Callable,
19
25
  Dict,
20
26
  Generic,
21
27
  List,
@@ -23,23 +29,23 @@ from typing import (
23
29
  Set,
24
30
  TypeVar,
25
31
  get_args,
32
+ get_origin,
33
+ get_type_hints,
26
34
  )
27
35
 
28
- from typing_extensions import Annotated, get_type_hints
29
-
30
- from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
31
- from reflex.utils.types import GenericType, is_union, value_inside_optional
32
-
33
- try:
34
- import pydantic.v1 as pydantic
35
- except ModuleNotFoundError:
36
- import pydantic
37
-
36
+ import pydantic.v1 as pydantic
38
37
  from reflex_cli.constants.hosting import Hosting
39
38
 
40
39
  from reflex import constants
41
40
  from reflex.base import Base
42
41
  from reflex.utils import console
42
+ from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
43
+ from reflex.utils.types import GenericType, is_union, value_inside_optional
44
+
45
+ try:
46
+ from dotenv import load_dotenv # pyright: ignore [reportMissingImports]
47
+ except ImportError:
48
+ load_dotenv = None
43
49
 
44
50
 
45
51
  class DBConfig(Base):
@@ -304,6 +310,15 @@ def interpret_env_var_value(
304
310
  return interpret_path_env(value, field_name)
305
311
  elif field_type is ExistingPath:
306
312
  return interpret_existing_path_env(value, field_name)
313
+ elif get_origin(field_type) is list:
314
+ return [
315
+ interpret_env_var_value(
316
+ v,
317
+ get_args(field_type)[0],
318
+ f"{field_name}[{i}]",
319
+ )
320
+ for i, v in enumerate(value.split(":"))
321
+ ]
307
322
  elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
308
323
  return interpret_enum_env(value, field_type, field_name)
309
324
 
@@ -387,7 +402,24 @@ class EnvVar(Generic[T]):
387
402
  else:
388
403
  if isinstance(value, enum.Enum):
389
404
  value = value.value
390
- os.environ[self.name] = str(value)
405
+ if isinstance(value, list):
406
+ str_value = ":".join(str(v) for v in value)
407
+ else:
408
+ str_value = str(value)
409
+ os.environ[self.name] = str_value
410
+
411
+
412
+ @lru_cache()
413
+ def get_type_hints_environment(cls: type) -> dict[str, Any]:
414
+ """Get the type hints for the environment variables.
415
+
416
+ Args:
417
+ cls: The class.
418
+
419
+ Returns:
420
+ The type hints.
421
+ """
422
+ return get_type_hints(cls)
391
423
 
392
424
 
393
425
  class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
@@ -416,7 +448,9 @@ class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
416
448
  """
417
449
  self.name = name
418
450
 
419
- def __get__(self, instance: Any, owner: Any):
451
+ def __get__(
452
+ self, instance: EnvironmentVariables, owner: type[EnvironmentVariables]
453
+ ):
420
454
  """Get the EnvVar instance.
421
455
 
422
456
  Args:
@@ -426,7 +460,7 @@ class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
426
460
  Returns:
427
461
  The EnvVar instance.
428
462
  """
429
- type_ = get_args(get_type_hints(owner)[self.name])[0]
463
+ type_ = get_args(get_type_hints_environment(owner)[self.name])[0]
430
464
  env_name = self.name
431
465
  if self.internal:
432
466
  env_name = f"__{env_name}"
@@ -463,9 +497,103 @@ class PerformanceMode(enum.Enum):
463
497
  OFF = "off"
464
498
 
465
499
 
500
+ class ExecutorType(enum.Enum):
501
+ """Executor for compiling the frontend."""
502
+
503
+ THREAD = "thread"
504
+ PROCESS = "process"
505
+ MAIN_THREAD = "main_thread"
506
+
507
+ @classmethod
508
+ def get_executor_from_environment(cls):
509
+ """Get the executor based on the environment variables.
510
+
511
+ Returns:
512
+ The executor.
513
+ """
514
+ executor_type = environment.REFLEX_COMPILE_EXECUTOR.get()
515
+
516
+ reflex_compile_processes = environment.REFLEX_COMPILE_PROCESSES.get()
517
+ reflex_compile_threads = environment.REFLEX_COMPILE_THREADS.get()
518
+ # By default, use the main thread. Unless the user has specified a different executor.
519
+ # Using a process pool is much faster, but not supported on all platforms. It's gated behind a flag.
520
+ if executor_type is None:
521
+ if (
522
+ platform.system() not in ("Linux", "Darwin")
523
+ and reflex_compile_processes is not None
524
+ ):
525
+ console.warn("Multiprocessing is only supported on Linux and MacOS.")
526
+
527
+ if (
528
+ platform.system() in ("Linux", "Darwin")
529
+ and reflex_compile_processes is not None
530
+ ):
531
+ if reflex_compile_processes == 0:
532
+ console.warn(
533
+ "Number of processes must be greater than 0. If you want to use the default number of processes, set REFLEX_COMPILE_EXECUTOR to 'process'. Defaulting to None."
534
+ )
535
+ reflex_compile_processes = None
536
+ elif reflex_compile_processes < 0:
537
+ console.warn(
538
+ "Number of processes must be greater than 0. Defaulting to None."
539
+ )
540
+ reflex_compile_processes = None
541
+ executor_type = ExecutorType.PROCESS
542
+ elif reflex_compile_threads is not None:
543
+ if reflex_compile_threads == 0:
544
+ console.warn(
545
+ "Number of threads must be greater than 0. If you want to use the default number of threads, set REFLEX_COMPILE_EXECUTOR to 'thread'. Defaulting to None."
546
+ )
547
+ reflex_compile_threads = None
548
+ elif reflex_compile_threads < 0:
549
+ console.warn(
550
+ "Number of threads must be greater than 0. Defaulting to None."
551
+ )
552
+ reflex_compile_threads = None
553
+ executor_type = ExecutorType.THREAD
554
+ else:
555
+ executor_type = ExecutorType.MAIN_THREAD
556
+
557
+ match executor_type:
558
+ case ExecutorType.PROCESS:
559
+ executor = concurrent.futures.ProcessPoolExecutor(
560
+ max_workers=reflex_compile_processes,
561
+ mp_context=multiprocessing.get_context("fork"),
562
+ )
563
+ case ExecutorType.THREAD:
564
+ executor = concurrent.futures.ThreadPoolExecutor(
565
+ max_workers=reflex_compile_threads
566
+ )
567
+ case ExecutorType.MAIN_THREAD:
568
+ FUTURE_RESULT_TYPE = TypeVar("FUTURE_RESULT_TYPE")
569
+
570
+ class MainThreadExecutor:
571
+ def __enter__(self):
572
+ return self
573
+
574
+ def __exit__(self, *args):
575
+ pass
576
+
577
+ def submit(
578
+ self, fn: Callable[..., FUTURE_RESULT_TYPE], *args, **kwargs
579
+ ) -> concurrent.futures.Future[FUTURE_RESULT_TYPE]:
580
+ future_job = concurrent.futures.Future()
581
+ future_job.set_result(fn(*args, **kwargs))
582
+ return future_job
583
+
584
+ executor = MainThreadExecutor()
585
+
586
+ return executor
587
+
588
+
466
589
  class EnvironmentVariables:
467
590
  """Environment variables class to instantiate environment variables."""
468
591
 
592
+ # Indicate the current command that was invoked in the reflex CLI.
593
+ REFLEX_COMPILE_CONTEXT: EnvVar[constants.CompileContext] = env_var(
594
+ constants.CompileContext.UNDEFINED, internal=True
595
+ )
596
+
469
597
  # Whether to use npm over bun to install frontend packages.
470
598
  REFLEX_USE_NPM: EnvVar[bool] = env_var(False)
471
599
 
@@ -504,6 +632,8 @@ class EnvironmentVariables:
504
632
  Path(constants.Dirs.UPLOADED_FILES)
505
633
  )
506
634
 
635
+ REFLEX_COMPILE_EXECUTOR: EnvVar[Optional[ExecutorType]] = env_var(None)
636
+
507
637
  # Whether to use separate processes to compile the frontend and how many. If not set, defaults to thread executor.
508
638
  REFLEX_COMPILE_PROCESSES: EnvVar[Optional[int]] = env_var(None)
509
639
 
@@ -511,7 +641,7 @@ class EnvironmentVariables:
511
641
  REFLEX_COMPILE_THREADS: EnvVar[Optional[int]] = env_var(None)
512
642
 
513
643
  # The directory to store reflex dependencies.
514
- REFLEX_DIR: EnvVar[Path] = env_var(Path(constants.Reflex.DIR))
644
+ REFLEX_DIR: EnvVar[Path] = env_var(constants.Reflex.DIR)
515
645
 
516
646
  # Whether to print the SQL queries if the log level is INFO or lower.
517
647
  SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False)
@@ -544,6 +674,12 @@ class EnvironmentVariables:
544
674
  # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY.
545
675
  REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False)
546
676
 
677
+ # The port to run the frontend on.
678
+ REFLEX_FRONTEND_PORT: EnvVar[int | None] = env_var(None)
679
+
680
+ # The port to run the backend on.
681
+ REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None)
682
+
547
683
  # Reflex internal env to reload the config.
548
684
  RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True)
549
685
 
@@ -563,7 +699,7 @@ class EnvironmentVariables:
563
699
  REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True)
564
700
 
565
701
  # In which performance mode to run the app.
566
- REFLEX_PERF_MODE: EnvVar[Optional[PerformanceMode]] = env_var(PerformanceMode.WARN)
702
+ REFLEX_PERF_MODE: EnvVar[PerformanceMode] = env_var(PerformanceMode.WARN)
567
703
 
568
704
  # The maximum size of the reflex state in kilobytes.
569
705
  REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000)
@@ -571,6 +707,21 @@ class EnvironmentVariables:
571
707
  # Whether to use the turbopack bundler.
572
708
  REFLEX_USE_TURBOPACK: EnvVar[bool] = env_var(True)
573
709
 
710
+ # Additional paths to include in the hot reload. Separated by a colon.
711
+ REFLEX_HOT_RELOAD_INCLUDE_PATHS: EnvVar[List[Path]] = env_var([])
712
+
713
+ # Paths to exclude from the hot reload. Takes precedence over include paths. Separated by a colon.
714
+ REFLEX_HOT_RELOAD_EXCLUDE_PATHS: EnvVar[List[Path]] = env_var([])
715
+
716
+ # Enables different behavior for when the backend would do a cold start if it was inactive.
717
+ REFLEX_DOES_BACKEND_COLD_START: EnvVar[bool] = env_var(False)
718
+
719
+ # The timeout for the backend to do a cold start in seconds.
720
+ REFLEX_BACKEND_COLD_START_TIMEOUT: EnvVar[int] = env_var(10)
721
+
722
+ # Used by flexgen to enumerate the pages.
723
+ REFLEX_ADD_ALL_ROUTES_ENDPOINT: EnvVar[bool] = env_var(False)
724
+
574
725
 
575
726
  environment = EnvironmentVariables()
576
727
 
@@ -604,6 +755,7 @@ class Config(Base):
604
755
  """Pydantic config for the config."""
605
756
 
606
757
  validate_assignment = True
758
+ use_enum_values = False
607
759
 
608
760
  # The name of the app (should match the name of the app directory).
609
761
  app_name: str
@@ -615,19 +767,21 @@ class Config(Base):
615
767
  loglevel: constants.LogLevel = constants.LogLevel.DEFAULT
616
768
 
617
769
  # The port to run the frontend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
618
- frontend_port: int = constants.DefaultPorts.FRONTEND_PORT
770
+ frontend_port: int | None = None
619
771
 
620
772
  # The path to run the frontend on. For example, "/app" will run the frontend on http://localhost:3000/app
621
773
  frontend_path: str = ""
622
774
 
623
775
  # The port to run the backend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
624
- backend_port: int = constants.DefaultPorts.BACKEND_PORT
776
+ backend_port: int | None = None
625
777
 
626
778
  # The backend url the frontend will connect to. This must be updated if the backend is hosted elsewhere, or in production.
627
- api_url: str = f"http://localhost:{backend_port}"
779
+ api_url: str = f"http://localhost:{constants.DefaultPorts.BACKEND_PORT}"
628
780
 
629
781
  # The url the frontend will be hosted on.
630
- deploy_url: Optional[str] = f"http://localhost:{frontend_port}"
782
+ deploy_url: Optional[str] = (
783
+ f"http://localhost:{constants.DefaultPorts.FRONTEND_PORT}"
784
+ )
631
785
 
632
786
  # The url the backend will be hosted on.
633
787
  backend_host: str = "0.0.0.0"
@@ -679,7 +833,7 @@ class Config(Base):
679
833
  # Number of gunicorn workers from user
680
834
  gunicorn_workers: Optional[int] = None
681
835
 
682
- # Number of requests before a worker is restarted
836
+ # Number of requests before a worker is restarted; set to 0 to disable
683
837
  gunicorn_max_requests: int = 100
684
838
 
685
839
  # Variance limit for max requests; gunicorn only
@@ -704,11 +858,14 @@ class Config(Base):
704
858
  env_file: Optional[str] = None
705
859
 
706
860
  # Whether to display the sticky "Built with Reflex" badge on all pages.
707
- show_built_with_reflex: bool = True
861
+ show_built_with_reflex: Optional[bool] = None
708
862
 
709
863
  # Whether the app is running in the reflex cloud environment.
710
864
  is_reflex_cloud: bool = False
711
865
 
866
+ # Extra overlay function to run after the app is built. Formatted such that `from path_0.path_1... import path[-1]`, and calling it with no arguments would work. For example, "reflex.components.moment.momnet".
867
+ extra_overlay_function: Optional[str] = None
868
+
712
869
  def __init__(self, *args, **kwargs):
713
870
  """Initialize the config values.
714
871
 
@@ -731,6 +888,9 @@ class Config(Base):
731
888
  self._non_default_attributes.update(kwargs)
732
889
  self._replace_defaults(**kwargs)
733
890
 
891
+ # Set the log level for this process
892
+ console.set_log_level(self.loglevel)
893
+
734
894
  if (
735
895
  self.state_manager_mode == constants.StateManagerMode.REDIS
736
896
  and not self.redis_url
@@ -770,16 +930,15 @@ class Config(Base):
770
930
  Returns:
771
931
  The updated config values.
772
932
  """
773
- if self.env_file:
774
- try:
775
- from dotenv import load_dotenv # pyright: ignore [reportMissingImports]
776
-
777
- # load env file if exists
778
- load_dotenv(self.env_file, override=True)
779
- except ImportError:
933
+ env_file = self.env_file or os.environ.get("ENV_FILE", None)
934
+ if env_file:
935
+ if load_dotenv is None:
780
936
  console.error(
781
937
  """The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.0.1"`."""
782
938
  )
939
+ else:
940
+ # load env file if exists
941
+ load_dotenv(env_file, override=True)
783
942
 
784
943
  updated_values = {}
785
944
  # Iterate over the fields.
@@ -25,6 +25,7 @@ from .base import (
25
25
  from .compiler import (
26
26
  NOCOMPILE_FILE,
27
27
  SETTER_PREFIX,
28
+ CompileContext,
28
29
  CompileVars,
29
30
  ComponentName,
30
31
  Ext,
@@ -65,6 +66,7 @@ __ALL__ = [
65
66
  ColorMode,
66
67
  Config,
67
68
  COOKIES,
69
+ CompileContext,
68
70
  ComponentName,
69
71
  CustomComponents,
70
72
  DefaultPage,
reflex/constants/base.py CHANGED
@@ -53,6 +53,12 @@ class Dirs(SimpleNamespace):
53
53
  POSTCSS_JS = "postcss.config.js"
54
54
  # The name of the states directory.
55
55
  STATES = ".states"
56
+ # Where compilation artifacts for the backend are stored.
57
+ BACKEND = "backend"
58
+ # JSON-encoded list of page routes that need to be evaluated on the backend.
59
+ STATEFUL_PAGES = "stateful_pages.json"
60
+ # Marker file indicating that upload component was used in the frontend.
61
+ UPLOAD_IS_USED = "upload_is_used"
56
62
 
57
63
 
58
64
  class Reflex(SimpleNamespace):
@@ -111,6 +111,15 @@ class ComponentName(Enum):
111
111
  return self.value.lower() + Ext.ZIP
112
112
 
113
113
 
114
+ class CompileContext(str, Enum):
115
+ """The context in which the compiler is running."""
116
+
117
+ RUN = "run"
118
+ EXPORT = "export"
119
+ DEPLOY = "deploy"
120
+ UNDEFINED = "undefined"
121
+
122
+
114
123
  class Imports(SimpleNamespace):
115
124
  """Common sets of import vars."""
116
125