reflex 0.7.13a1__py3-none-any.whl → 0.7.14__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 (263) hide show
  1. reflex/.templates/apps/blank/code/blank.py +0 -2
  2. reflex/app.py +100 -92
  3. reflex/app_mixins/lifespan.py +2 -3
  4. reflex/app_mixins/middleware.py +1 -0
  5. reflex/app_mixins/mixin.py +0 -1
  6. reflex/assets.py +7 -4
  7. reflex/base.py +3 -2
  8. reflex/compiler/compiler.py +79 -65
  9. reflex/compiler/utils.py +8 -6
  10. reflex/components/base/app_wrap.pyi +0 -1
  11. reflex/components/base/bare.py +22 -12
  12. reflex/components/base/body.pyi +0 -1
  13. reflex/components/base/document.pyi +0 -5
  14. reflex/components/base/error_boundary.pyi +0 -1
  15. reflex/components/base/fragment.pyi +0 -1
  16. reflex/components/base/head.pyi +0 -2
  17. reflex/components/base/link.pyi +0 -2
  18. reflex/components/base/meta.py +2 -1
  19. reflex/components/base/meta.pyi +0 -4
  20. reflex/components/base/script.py +2 -1
  21. reflex/components/base/script.pyi +0 -1
  22. reflex/components/base/strict_mode.pyi +0 -1
  23. reflex/components/component.py +85 -45
  24. reflex/components/core/auto_scroll.pyi +0 -1
  25. reflex/components/core/banner.py +1 -1
  26. reflex/components/core/banner.pyi +0 -6
  27. reflex/components/core/breakpoints.py +9 -11
  28. reflex/components/core/client_side_routing.pyi +0 -2
  29. reflex/components/core/clipboard.pyi +0 -1
  30. reflex/components/core/colors.py +10 -7
  31. reflex/components/core/cond.py +4 -2
  32. reflex/components/core/debounce.py +5 -3
  33. reflex/components/core/debounce.pyi +0 -1
  34. reflex/components/core/foreach.py +8 -6
  35. reflex/components/core/html.py +3 -3
  36. reflex/components/core/html.pyi +0 -1
  37. reflex/components/core/match.py +19 -17
  38. reflex/components/core/sticky.pyi +0 -4
  39. reflex/components/core/upload.py +1 -1
  40. reflex/components/core/upload.pyi +0 -5
  41. reflex/components/datadisplay/code.py +1 -2
  42. reflex/components/datadisplay/code.pyi +0 -2
  43. reflex/components/datadisplay/dataeditor.py +7 -10
  44. reflex/components/datadisplay/dataeditor.pyi +0 -1
  45. reflex/components/datadisplay/logo.py +3 -4
  46. reflex/components/datadisplay/shiki_code_block.py +8 -11
  47. reflex/components/datadisplay/shiki_code_block.pyi +0 -3
  48. reflex/components/dynamic.py +2 -3
  49. reflex/components/el/__init__.pyi +2 -0
  50. reflex/components/el/element.pyi +0 -1
  51. reflex/components/el/elements/__init__.py +1 -0
  52. reflex/components/el/elements/__init__.pyi +3 -0
  53. reflex/components/el/elements/base.pyi +0 -1
  54. reflex/components/el/elements/forms.py +14 -15
  55. reflex/components/el/elements/forms.pyi +15 -32
  56. reflex/components/el/elements/inline.pyi +0 -28
  57. reflex/components/el/elements/media.py +26 -0
  58. reflex/components/el/elements/media.pyi +259 -25
  59. reflex/components/el/elements/metadata.py +0 -1
  60. reflex/components/el/elements/metadata.pyi +0 -6
  61. reflex/components/el/elements/other.pyi +0 -7
  62. reflex/components/el/elements/scripts.pyi +0 -3
  63. reflex/components/el/elements/sectioning.pyi +0 -15
  64. reflex/components/el/elements/tables.pyi +0 -10
  65. reflex/components/el/elements/typography.pyi +0 -15
  66. reflex/components/gridjs/datatable.py +10 -13
  67. reflex/components/gridjs/datatable.pyi +0 -2
  68. reflex/components/lucide/icon.py +10 -9
  69. reflex/components/lucide/icon.pyi +0 -3
  70. reflex/components/markdown/markdown.py +6 -8
  71. reflex/components/markdown/markdown.pyi +0 -1
  72. reflex/components/moment/moment.pyi +0 -1
  73. reflex/components/next/base.py +0 -2
  74. reflex/components/next/base.pyi +0 -3
  75. reflex/components/next/image.pyi +0 -1
  76. reflex/components/next/link.pyi +0 -1
  77. reflex/components/next/video.pyi +0 -1
  78. reflex/components/plotly/plotly.pyi +0 -9
  79. reflex/components/props.py +4 -3
  80. reflex/components/radix/primitives/accordion.py +1 -1
  81. reflex/components/radix/primitives/accordion.pyi +0 -7
  82. reflex/components/radix/primitives/base.py +1 -3
  83. reflex/components/radix/primitives/base.pyi +0 -2
  84. reflex/components/radix/primitives/drawer.py +1 -1
  85. reflex/components/radix/primitives/drawer.pyi +0 -11
  86. reflex/components/radix/primitives/form.py +5 -9
  87. reflex/components/radix/primitives/form.pyi +0 -12
  88. reflex/components/radix/primitives/progress.py +1 -1
  89. reflex/components/radix/primitives/progress.pyi +0 -5
  90. reflex/components/radix/primitives/slider.py +1 -1
  91. reflex/components/radix/primitives/slider.pyi +0 -5
  92. reflex/components/radix/themes/base.pyi +0 -8
  93. reflex/components/radix/themes/color_mode.pyi +0 -3
  94. reflex/components/radix/themes/components/alert_dialog.py +4 -2
  95. reflex/components/radix/themes/components/alert_dialog.pyi +4 -9
  96. reflex/components/radix/themes/components/aspect_ratio.py +1 -2
  97. reflex/components/radix/themes/components/aspect_ratio.pyi +1 -3
  98. reflex/components/radix/themes/components/avatar.py +5 -2
  99. reflex/components/radix/themes/components/avatar.pyi +1 -3
  100. reflex/components/radix/themes/components/badge.py +5 -2
  101. reflex/components/radix/themes/components/badge.pyi +1 -3
  102. reflex/components/radix/themes/components/button.py +2 -3
  103. reflex/components/radix/themes/components/button.pyi +1 -3
  104. reflex/components/radix/themes/components/callout.py +1 -2
  105. reflex/components/radix/themes/components/callout.pyi +1 -7
  106. reflex/components/radix/themes/components/card.py +1 -2
  107. reflex/components/radix/themes/components/card.pyi +1 -3
  108. reflex/components/radix/themes/components/checkbox.py +7 -4
  109. reflex/components/radix/themes/components/checkbox.pyi +1 -5
  110. reflex/components/radix/themes/components/checkbox_cards.py +1 -2
  111. reflex/components/radix/themes/components/checkbox_cards.pyi +1 -4
  112. reflex/components/radix/themes/components/checkbox_group.py +1 -2
  113. reflex/components/radix/themes/components/checkbox_group.pyi +1 -4
  114. reflex/components/radix/themes/components/context_menu.py +1 -1
  115. reflex/components/radix/themes/components/context_menu.pyi +1 -14
  116. reflex/components/radix/themes/components/data_list.py +1 -2
  117. reflex/components/radix/themes/components/data_list.pyi +1 -6
  118. reflex/components/radix/themes/components/dialog.py +4 -2
  119. reflex/components/radix/themes/components/dialog.pyi +4 -9
  120. reflex/components/radix/themes/components/dropdown_menu.py +5 -2
  121. reflex/components/radix/themes/components/dropdown_menu.pyi +4 -10
  122. reflex/components/radix/themes/components/hover_card.py +4 -2
  123. reflex/components/radix/themes/components/hover_card.pyi +4 -6
  124. reflex/components/radix/themes/components/icon_button.py +7 -8
  125. reflex/components/radix/themes/components/icon_button.pyi +1 -3
  126. reflex/components/radix/themes/components/inset.py +1 -2
  127. reflex/components/radix/themes/components/inset.pyi +1 -3
  128. reflex/components/radix/themes/components/popover.py +4 -2
  129. reflex/components/radix/themes/components/popover.pyi +4 -6
  130. reflex/components/radix/themes/components/progress.py +1 -2
  131. reflex/components/radix/themes/components/progress.pyi +1 -3
  132. reflex/components/radix/themes/components/radio.py +1 -2
  133. reflex/components/radix/themes/components/radio.pyi +1 -3
  134. reflex/components/radix/themes/components/radio_cards.py +1 -2
  135. reflex/components/radix/themes/components/radio_cards.pyi +1 -4
  136. reflex/components/radix/themes/components/radio_group.py +7 -5
  137. reflex/components/radix/themes/components/radio_group.pyi +1 -6
  138. reflex/components/radix/themes/components/scroll_area.py +1 -2
  139. reflex/components/radix/themes/components/scroll_area.pyi +1 -3
  140. reflex/components/radix/themes/components/segmented_control.py +1 -2
  141. reflex/components/radix/themes/components/segmented_control.pyi +1 -4
  142. reflex/components/radix/themes/components/select.py +5 -2
  143. reflex/components/radix/themes/components/select.pyi +1 -11
  144. reflex/components/radix/themes/components/separator.py +1 -2
  145. reflex/components/radix/themes/components/separator.pyi +1 -3
  146. reflex/components/radix/themes/components/skeleton.py +1 -2
  147. reflex/components/radix/themes/components/skeleton.pyi +1 -3
  148. reflex/components/radix/themes/components/slider.py +1 -2
  149. reflex/components/radix/themes/components/slider.pyi +1 -3
  150. reflex/components/radix/themes/components/spinner.py +1 -2
  151. reflex/components/radix/themes/components/spinner.pyi +1 -3
  152. reflex/components/radix/themes/components/switch.py +1 -2
  153. reflex/components/radix/themes/components/switch.pyi +1 -3
  154. reflex/components/radix/themes/components/table.py +1 -2
  155. reflex/components/radix/themes/components/table.pyi +1 -9
  156. reflex/components/radix/themes/components/tabs.py +1 -2
  157. reflex/components/radix/themes/components/tabs.pyi +1 -7
  158. reflex/components/radix/themes/components/text_area.py +5 -2
  159. reflex/components/radix/themes/components/text_area.pyi +2 -4
  160. reflex/components/radix/themes/components/text_field.py +5 -2
  161. reflex/components/radix/themes/components/text_field.pyi +1 -5
  162. reflex/components/radix/themes/components/tooltip.py +1 -2
  163. reflex/components/radix/themes/components/tooltip.pyi +1 -3
  164. reflex/components/radix/themes/layout/base.py +5 -2
  165. reflex/components/radix/themes/layout/base.pyi +5 -3
  166. reflex/components/radix/themes/layout/box.py +1 -2
  167. reflex/components/radix/themes/layout/box.pyi +1 -3
  168. reflex/components/radix/themes/layout/center.pyi +0 -1
  169. reflex/components/radix/themes/layout/container.py +1 -2
  170. reflex/components/radix/themes/layout/container.pyi +1 -3
  171. reflex/components/radix/themes/layout/flex.py +6 -2
  172. reflex/components/radix/themes/layout/flex.pyi +1 -3
  173. reflex/components/radix/themes/layout/grid.py +6 -2
  174. reflex/components/radix/themes/layout/grid.pyi +1 -3
  175. reflex/components/radix/themes/layout/list.py +2 -1
  176. reflex/components/radix/themes/layout/list.pyi +0 -5
  177. reflex/components/radix/themes/layout/section.py +1 -2
  178. reflex/components/radix/themes/layout/section.pyi +1 -3
  179. reflex/components/radix/themes/layout/spacer.pyi +0 -1
  180. reflex/components/radix/themes/layout/stack.py +1 -1
  181. reflex/components/radix/themes/layout/stack.pyi +0 -3
  182. reflex/components/radix/themes/typography/blockquote.py +1 -1
  183. reflex/components/radix/themes/typography/blockquote.pyi +1 -3
  184. reflex/components/radix/themes/typography/code.py +5 -1
  185. reflex/components/radix/themes/typography/code.pyi +1 -3
  186. reflex/components/radix/themes/typography/heading.py +1 -1
  187. reflex/components/radix/themes/typography/heading.pyi +1 -3
  188. reflex/components/radix/themes/typography/link.py +3 -2
  189. reflex/components/radix/themes/typography/link.pyi +1 -3
  190. reflex/components/radix/themes/typography/text.py +1 -1
  191. reflex/components/radix/themes/typography/text.pyi +1 -9
  192. reflex/components/react_player/audio.py +0 -2
  193. reflex/components/react_player/audio.pyi +0 -3
  194. reflex/components/react_player/react_player.pyi +0 -1
  195. reflex/components/react_player/video.py +0 -2
  196. reflex/components/react_player/video.pyi +0 -3
  197. reflex/components/recharts/__init__.py +1 -1
  198. reflex/components/recharts/__init__.pyi +1 -1
  199. reflex/components/recharts/cartesian.py +20 -25
  200. reflex/components/recharts/cartesian.pyi +20 -37
  201. reflex/components/recharts/charts.py +2 -1
  202. reflex/components/recharts/charts.pyi +0 -12
  203. reflex/components/recharts/general.pyi +0 -6
  204. reflex/components/recharts/polar.py +5 -4
  205. reflex/components/recharts/polar.pyi +4 -10
  206. reflex/components/recharts/recharts.py +12 -10
  207. reflex/components/recharts/recharts.pyi +10 -11
  208. reflex/components/sonner/toast.py +2 -2
  209. reflex/components/sonner/toast.pyi +0 -2
  210. reflex/components/suneditor/editor.py +2 -1
  211. reflex/components/suneditor/editor.pyi +0 -1
  212. reflex/components/tags/iter_tag.py +4 -2
  213. reflex/config.py +41 -615
  214. reflex/constants/base.py +6 -6
  215. reflex/constants/compiler.py +8 -6
  216. reflex/constants/installer.py +25 -16
  217. reflex/custom_components/custom_components.py +1 -2
  218. reflex/environment.py +606 -0
  219. reflex/event.py +58 -60
  220. reflex/experimental/__init__.py +2 -2
  221. reflex/experimental/client_state.py +9 -4
  222. reflex/experimental/layout.pyi +0 -5
  223. reflex/istate/manager.py +17 -20
  224. reflex/istate/proxy.py +19 -12
  225. reflex/model.py +8 -5
  226. reflex/plugins/base.py +8 -0
  227. reflex/plugins/tailwind_v3.py +8 -0
  228. reflex/plugins/tailwind_v4.py +9 -0
  229. reflex/reflex.py +11 -12
  230. reflex/route.py +7 -9
  231. reflex/state.py +67 -71
  232. reflex/style.py +3 -1
  233. reflex/testing.py +49 -30
  234. reflex/utils/build.py +2 -1
  235. reflex/utils/console.py +70 -17
  236. reflex/utils/exec.py +113 -39
  237. reflex/utils/export.py +2 -1
  238. reflex/utils/format.py +21 -24
  239. reflex/utils/imports.py +4 -3
  240. reflex/utils/lazy_loader.py +3 -3
  241. reflex/utils/misc.py +2 -1
  242. reflex/utils/net.py +2 -2
  243. reflex/utils/path_ops.py +4 -2
  244. reflex/utils/prerequisites.py +69 -39
  245. reflex/utils/processes.py +5 -7
  246. reflex/utils/pyi_generator.py +46 -41
  247. reflex/utils/redir.py +1 -1
  248. reflex/utils/registry.py +1 -1
  249. reflex/utils/serializers.py +4 -4
  250. reflex/utils/telemetry.py +36 -3
  251. reflex/utils/types.py +16 -13
  252. reflex/vars/base.py +96 -109
  253. reflex/vars/datetime.py +2 -1
  254. reflex/vars/dep_tracking.py +19 -28
  255. reflex/vars/number.py +6 -7
  256. reflex/vars/object.py +5 -6
  257. reflex/vars/sequence.py +11 -11
  258. {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/METADATA +1 -1
  259. reflex-0.7.14.dist-info/RECORD +408 -0
  260. reflex-0.7.13a1.dist-info/RECORD +0 -407
  261. {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/WHEEL +0 -0
  262. {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/entry_points.txt +0 -0
  263. {reflex-0.7.13a1.dist-info → reflex-0.7.14.dist-info}/licenses/LICENSE +0 -0
reflex/config.py CHANGED
@@ -2,48 +2,30 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import concurrent.futures
6
- import dataclasses
7
- import enum
8
5
  import importlib
9
- import inspect
10
- import multiprocessing
11
6
  import os
12
- import platform
13
7
  import sys
14
8
  import threading
15
9
  import urllib.parse
16
- from collections.abc import Callable
17
- from functools import lru_cache
18
10
  from importlib.util import find_spec
19
11
  from pathlib import Path
20
12
  from types import ModuleType
21
- from typing import (
22
- TYPE_CHECKING,
23
- Annotated,
24
- Any,
25
- ClassVar,
26
- Generic,
27
- TypeVar,
28
- get_args,
29
- get_origin,
30
- get_type_hints,
31
- )
13
+ from typing import Any, ClassVar
32
14
 
33
15
  import pydantic.v1 as pydantic
34
16
 
35
17
  from reflex import constants
36
18
  from reflex.base import Base
37
19
  from reflex.constants.base import LogLevel
20
+ from reflex.environment import EnvironmentVariables as EnvironmentVariables
21
+ from reflex.environment import EnvVar as EnvVar
22
+ from reflex.environment import ExistingPath, interpret_env_var_value
23
+ from reflex.environment import env_var as env_var
24
+ from reflex.environment import environment as environment
38
25
  from reflex.plugins import Plugin, TailwindV3Plugin, TailwindV4Plugin
39
26
  from reflex.utils import console
40
- from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
41
- from reflex.utils.types import (
42
- GenericType,
43
- is_union,
44
- true_type_for_pydantic_field,
45
- value_inside_optional,
46
- )
27
+ from reflex.utils.exceptions import ConfigError
28
+ from reflex.utils.types import true_type_for_pydantic_field
47
29
 
48
30
  try:
49
31
  from dotenv import load_dotenv
@@ -69,9 +51,27 @@ def _load_dotenv_from_str(env_files: str) -> None:
69
51
  load_dotenv(env_file_path, override=True)
70
52
 
71
53
 
54
+ def _load_dotenv_from_env():
55
+ """Load environment variables from paths specified in REFLEX_ENV_FILE."""
56
+ show_deprecation = False
57
+ env_env_file = os.environ.get("REFLEX_ENV_FILE")
58
+ if not env_env_file:
59
+ env_env_file = os.environ.get("ENV_FILE")
60
+ if env_env_file:
61
+ show_deprecation = True
62
+ if show_deprecation:
63
+ console.deprecate(
64
+ "Usage of deprecated ENV_FILE env var detected.",
65
+ reason="Prefer `REFLEX_` prefix when setting env vars.",
66
+ deprecation_version="0.7.13",
67
+ removal_version="0.8.0",
68
+ )
69
+ if env_env_file:
70
+ _load_dotenv_from_str(env_env_file)
71
+
72
+
72
73
  # Load the env files at import time if they are set in the ENV_FILE environment variable.
73
- if env_files := os.getenv("ENV_FILE"):
74
- _load_dotenv_from_str(env_files)
74
+ _load_dotenv_from_env()
75
75
 
76
76
 
77
77
  class DBConfig(Base):
@@ -182,581 +182,6 @@ class DBConfig(Base):
182
182
  return f"{self.engine}://{path}/{self.database}"
183
183
 
184
184
 
185
- def get_default_value_for_field(field: dataclasses.Field) -> Any:
186
- """Get the default value for a field.
187
-
188
- Args:
189
- field: The field.
190
-
191
- Returns:
192
- The default value.
193
-
194
- Raises:
195
- ValueError: If no default value is found.
196
- """
197
- if field.default != dataclasses.MISSING:
198
- return field.default
199
- elif field.default_factory != dataclasses.MISSING:
200
- return field.default_factory()
201
- else:
202
- raise ValueError(
203
- f"Missing value for environment variable {field.name} and no default value found"
204
- )
205
-
206
-
207
- # TODO: Change all interpret_.* signatures to value: str, field: dataclasses.Field once we migrate rx.Config to dataclasses
208
- def interpret_boolean_env(value: str, field_name: str) -> bool:
209
- """Interpret a boolean environment variable value.
210
-
211
- Args:
212
- value: The environment variable value.
213
- field_name: The field name.
214
-
215
- Returns:
216
- The interpreted value.
217
-
218
- Raises:
219
- EnvironmentVarValueError: If the value is invalid.
220
- """
221
- true_values = ["true", "1", "yes", "y"]
222
- false_values = ["false", "0", "no", "n"]
223
-
224
- if value.lower() in true_values:
225
- return True
226
- elif value.lower() in false_values:
227
- return False
228
- raise EnvironmentVarValueError(f"Invalid boolean value: {value} for {field_name}")
229
-
230
-
231
- def interpret_int_env(value: str, field_name: str) -> int:
232
- """Interpret an integer environment variable value.
233
-
234
- Args:
235
- value: The environment variable value.
236
- field_name: The field name.
237
-
238
- Returns:
239
- The interpreted value.
240
-
241
- Raises:
242
- EnvironmentVarValueError: If the value is invalid.
243
- """
244
- try:
245
- return int(value)
246
- except ValueError as ve:
247
- raise EnvironmentVarValueError(
248
- f"Invalid integer value: {value} for {field_name}"
249
- ) from ve
250
-
251
-
252
- def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
253
- """Interpret a path environment variable value as an existing path.
254
-
255
- Args:
256
- value: The environment variable value.
257
- field_name: The field name.
258
-
259
- Returns:
260
- The interpreted value.
261
-
262
- Raises:
263
- EnvironmentVarValueError: If the path does not exist.
264
- """
265
- path = Path(value)
266
- if not path.exists():
267
- raise EnvironmentVarValueError(f"Path does not exist: {path} for {field_name}")
268
- return path
269
-
270
-
271
- def interpret_path_env(value: str, field_name: str) -> Path:
272
- """Interpret a path environment variable value.
273
-
274
- Args:
275
- value: The environment variable value.
276
- field_name: The field name.
277
-
278
- Returns:
279
- The interpreted value.
280
- """
281
- return Path(value)
282
-
283
-
284
- def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any:
285
- """Interpret an enum environment variable value.
286
-
287
- Args:
288
- value: The environment variable value.
289
- field_type: The field type.
290
- field_name: The field name.
291
-
292
- Returns:
293
- The interpreted value.
294
-
295
- Raises:
296
- EnvironmentVarValueError: If the value is invalid.
297
- """
298
- try:
299
- return field_type(value)
300
- except ValueError as ve:
301
- raise EnvironmentVarValueError(
302
- f"Invalid enum value: {value} for {field_name}"
303
- ) from ve
304
-
305
-
306
- def interpret_env_var_value(
307
- value: str, field_type: GenericType, field_name: str
308
- ) -> Any:
309
- """Interpret an environment variable value based on the field type.
310
-
311
- Args:
312
- value: The environment variable value.
313
- field_type: The field type.
314
- field_name: The field name.
315
-
316
- Returns:
317
- The interpreted value.
318
-
319
- Raises:
320
- ValueError: If the value is invalid.
321
- """
322
- field_type = value_inside_optional(field_type)
323
-
324
- if is_union(field_type):
325
- raise ValueError(
326
- f"Union types are not supported for environment variables: {field_name}."
327
- )
328
-
329
- if field_type is bool:
330
- return interpret_boolean_env(value, field_name)
331
- elif field_type is str:
332
- return value
333
- elif field_type is int:
334
- return interpret_int_env(value, field_name)
335
- elif field_type is Path:
336
- return interpret_path_env(value, field_name)
337
- elif field_type is ExistingPath:
338
- return interpret_existing_path_env(value, field_name)
339
- elif get_origin(field_type) is list:
340
- return [
341
- interpret_env_var_value(
342
- v,
343
- get_args(field_type)[0],
344
- f"{field_name}[{i}]",
345
- )
346
- for i, v in enumerate(value.split(":"))
347
- ]
348
- elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
349
- return interpret_enum_env(value, field_type, field_name)
350
-
351
- else:
352
- raise ValueError(
353
- f"Invalid type for environment variable {field_name}: {field_type}. This is probably an issue in Reflex."
354
- )
355
-
356
-
357
- T = TypeVar("T")
358
-
359
-
360
- class EnvVar(Generic[T]):
361
- """Environment variable."""
362
-
363
- name: str
364
- default: Any
365
- type_: T
366
-
367
- def __init__(self, name: str, default: Any, type_: T) -> None:
368
- """Initialize the environment variable.
369
-
370
- Args:
371
- name: The environment variable name.
372
- default: The default value.
373
- type_: The type of the value.
374
- """
375
- self.name = name
376
- self.default = default
377
- self.type_ = type_
378
-
379
- def interpret(self, value: str) -> T:
380
- """Interpret the environment variable value.
381
-
382
- Args:
383
- value: The environment variable value.
384
-
385
- Returns:
386
- The interpreted value.
387
- """
388
- return interpret_env_var_value(value, self.type_, self.name)
389
-
390
- def getenv(self) -> T | None:
391
- """Get the interpreted environment variable value.
392
-
393
- Returns:
394
- The environment variable value.
395
- """
396
- env_value = os.getenv(self.name, None)
397
- if env_value and env_value.strip():
398
- return self.interpret(env_value)
399
- return None
400
-
401
- def is_set(self) -> bool:
402
- """Check if the environment variable is set.
403
-
404
- Returns:
405
- True if the environment variable is set.
406
- """
407
- return bool(os.getenv(self.name, "").strip())
408
-
409
- def get(self) -> T:
410
- """Get the interpreted environment variable value or the default value if not set.
411
-
412
- Returns:
413
- The interpreted value.
414
- """
415
- env_value = self.getenv()
416
- if env_value is not None:
417
- return env_value
418
- return self.default
419
-
420
- def set(self, value: T | None) -> None:
421
- """Set the environment variable. None unsets the variable.
422
-
423
- Args:
424
- value: The value to set.
425
- """
426
- if value is None:
427
- _ = os.environ.pop(self.name, None)
428
- else:
429
- if isinstance(value, enum.Enum):
430
- value = value.value
431
- if isinstance(value, list):
432
- str_value = ":".join(str(v) for v in value)
433
- else:
434
- str_value = str(value)
435
- os.environ[self.name] = str_value
436
-
437
-
438
- @lru_cache
439
- def get_type_hints_environment(cls: type) -> dict[str, Any]:
440
- """Get the type hints for the environment variables.
441
-
442
- Args:
443
- cls: The class.
444
-
445
- Returns:
446
- The type hints.
447
- """
448
- return get_type_hints(cls)
449
-
450
-
451
- class env_var: # noqa: N801 # pyright: ignore [reportRedeclaration]
452
- """Descriptor for environment variables."""
453
-
454
- name: str
455
- default: Any
456
- internal: bool = False
457
-
458
- def __init__(self, default: Any, internal: bool = False) -> None:
459
- """Initialize the descriptor.
460
-
461
- Args:
462
- default: The default value.
463
- internal: Whether the environment variable is reflex internal.
464
- """
465
- self.default = default
466
- self.internal = internal
467
-
468
- def __set_name__(self, owner: Any, name: str):
469
- """Set the name of the descriptor.
470
-
471
- Args:
472
- owner: The owner class.
473
- name: The name of the descriptor.
474
- """
475
- self.name = name
476
-
477
- def __get__(
478
- self, instance: EnvironmentVariables, owner: type[EnvironmentVariables]
479
- ):
480
- """Get the EnvVar instance.
481
-
482
- Args:
483
- instance: The instance.
484
- owner: The owner class.
485
-
486
- Returns:
487
- The EnvVar instance.
488
- """
489
- type_ = get_args(get_type_hints_environment(owner)[self.name])[0]
490
- env_name = self.name
491
- if self.internal:
492
- env_name = f"__{env_name}"
493
- return EnvVar(name=env_name, default=self.default, type_=type_)
494
-
495
-
496
- if TYPE_CHECKING:
497
-
498
- def env_var(default: Any, internal: bool = False) -> EnvVar:
499
- """Typing helper for the env_var descriptor.
500
-
501
- Args:
502
- default: The default value.
503
- internal: Whether the environment variable is reflex internal.
504
-
505
- Returns:
506
- The EnvVar instance.
507
- """
508
- return default
509
-
510
-
511
- class PathExistsFlag:
512
- """Flag to indicate that a path must exist."""
513
-
514
-
515
- ExistingPath = Annotated[Path, PathExistsFlag]
516
-
517
-
518
- class PerformanceMode(enum.Enum):
519
- """Performance mode for the app."""
520
-
521
- WARN = "warn"
522
- RAISE = "raise"
523
- OFF = "off"
524
-
525
-
526
- class ExecutorType(enum.Enum):
527
- """Executor for compiling the frontend."""
528
-
529
- THREAD = "thread"
530
- PROCESS = "process"
531
- MAIN_THREAD = "main_thread"
532
-
533
- @classmethod
534
- def get_executor_from_environment(cls):
535
- """Get the executor based on the environment variables.
536
-
537
- Returns:
538
- The executor.
539
- """
540
- executor_type = environment.REFLEX_COMPILE_EXECUTOR.get()
541
-
542
- reflex_compile_processes = environment.REFLEX_COMPILE_PROCESSES.get()
543
- reflex_compile_threads = environment.REFLEX_COMPILE_THREADS.get()
544
- # By default, use the main thread. Unless the user has specified a different executor.
545
- # Using a process pool is much faster, but not supported on all platforms. It's gated behind a flag.
546
- if executor_type is None:
547
- if (
548
- platform.system() not in ("Linux", "Darwin")
549
- and reflex_compile_processes is not None
550
- ):
551
- console.warn("Multiprocessing is only supported on Linux and MacOS.")
552
-
553
- if (
554
- platform.system() in ("Linux", "Darwin")
555
- and reflex_compile_processes is not None
556
- ):
557
- if reflex_compile_processes == 0:
558
- console.warn(
559
- "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."
560
- )
561
- reflex_compile_processes = None
562
- elif reflex_compile_processes < 0:
563
- console.warn(
564
- "Number of processes must be greater than 0. Defaulting to None."
565
- )
566
- reflex_compile_processes = None
567
- executor_type = ExecutorType.PROCESS
568
- elif reflex_compile_threads is not None:
569
- if reflex_compile_threads == 0:
570
- console.warn(
571
- "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."
572
- )
573
- reflex_compile_threads = None
574
- elif reflex_compile_threads < 0:
575
- console.warn(
576
- "Number of threads must be greater than 0. Defaulting to None."
577
- )
578
- reflex_compile_threads = None
579
- executor_type = ExecutorType.THREAD
580
- else:
581
- executor_type = ExecutorType.MAIN_THREAD
582
-
583
- match executor_type:
584
- case ExecutorType.PROCESS:
585
- executor = concurrent.futures.ProcessPoolExecutor(
586
- max_workers=reflex_compile_processes,
587
- mp_context=multiprocessing.get_context("fork"),
588
- )
589
- case ExecutorType.THREAD:
590
- executor = concurrent.futures.ThreadPoolExecutor(
591
- max_workers=reflex_compile_threads
592
- )
593
- case ExecutorType.MAIN_THREAD:
594
- FUTURE_RESULT_TYPE = TypeVar("FUTURE_RESULT_TYPE")
595
-
596
- class MainThreadExecutor:
597
- def __enter__(self):
598
- return self
599
-
600
- def __exit__(self, *args):
601
- pass
602
-
603
- def submit(
604
- self, fn: Callable[..., FUTURE_RESULT_TYPE], *args, **kwargs
605
- ) -> concurrent.futures.Future[FUTURE_RESULT_TYPE]:
606
- future_job = concurrent.futures.Future()
607
- future_job.set_result(fn(*args, **kwargs))
608
- return future_job
609
-
610
- executor = MainThreadExecutor()
611
-
612
- return executor
613
-
614
-
615
- class EnvironmentVariables:
616
- """Environment variables class to instantiate environment variables."""
617
-
618
- # Indicate the current command that was invoked in the reflex CLI.
619
- REFLEX_COMPILE_CONTEXT: EnvVar[constants.CompileContext] = env_var(
620
- constants.CompileContext.UNDEFINED, internal=True
621
- )
622
-
623
- # Whether to use npm over bun to install and run the frontend.
624
- REFLEX_USE_NPM: EnvVar[bool] = env_var(False)
625
-
626
- # The npm registry to use.
627
- NPM_CONFIG_REGISTRY: EnvVar[str | None] = env_var(None)
628
-
629
- # Whether to use Granian for the backend. By default, the backend uses Uvicorn if available.
630
- REFLEX_USE_GRANIAN: EnvVar[bool] = env_var(False)
631
-
632
- # Whether to use the system installed bun. If set to false, bun will be bundled with the app.
633
- REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False)
634
-
635
- # The working directory for the next.js commands.
636
- REFLEX_WEB_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.WEB))
637
-
638
- # The working directory for the states directory.
639
- REFLEX_STATES_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.STATES))
640
-
641
- # Path to the alembic config file
642
- ALEMBIC_CONFIG: EnvVar[ExistingPath] = env_var(Path(constants.ALEMBIC_CONFIG))
643
-
644
- # Disable SSL verification for HTTPX requests.
645
- SSL_NO_VERIFY: EnvVar[bool] = env_var(False)
646
-
647
- # The directory to store uploaded files.
648
- REFLEX_UPLOADED_FILES_DIR: EnvVar[Path] = env_var(
649
- Path(constants.Dirs.UPLOADED_FILES)
650
- )
651
-
652
- REFLEX_COMPILE_EXECUTOR: EnvVar[ExecutorType | None] = env_var(None)
653
-
654
- # Whether to use separate processes to compile the frontend and how many. If not set, defaults to thread executor.
655
- REFLEX_COMPILE_PROCESSES: EnvVar[int | None] = env_var(None)
656
-
657
- # Whether to use separate threads to compile the frontend and how many. Defaults to `min(32, os.cpu_count() + 4)`.
658
- REFLEX_COMPILE_THREADS: EnvVar[int | None] = env_var(None)
659
-
660
- # The directory to store reflex dependencies.
661
- REFLEX_DIR: EnvVar[Path] = env_var(constants.Reflex.DIR)
662
-
663
- # Whether to print the SQL queries if the log level is INFO or lower.
664
- SQLALCHEMY_ECHO: EnvVar[bool] = env_var(False)
665
-
666
- # Whether to check db connections before using them.
667
- SQLALCHEMY_POOL_PRE_PING: EnvVar[bool] = env_var(True)
668
-
669
- # Whether to ignore the redis config error. Some redis servers only allow out-of-band configuration.
670
- REFLEX_IGNORE_REDIS_CONFIG_ERROR: EnvVar[bool] = env_var(False)
671
-
672
- # Whether to skip purging the web directory in dev mode.
673
- REFLEX_PERSIST_WEB_DIR: EnvVar[bool] = env_var(False)
674
-
675
- # The reflex.build frontend host.
676
- REFLEX_BUILD_FRONTEND: EnvVar[str] = env_var(
677
- constants.Templates.REFLEX_BUILD_FRONTEND
678
- )
679
-
680
- # The reflex.build backend host.
681
- REFLEX_BUILD_BACKEND: EnvVar[str] = env_var(
682
- constants.Templates.REFLEX_BUILD_BACKEND
683
- )
684
-
685
- # This env var stores the execution mode of the app
686
- REFLEX_ENV_MODE: EnvVar[constants.Env] = env_var(constants.Env.DEV)
687
-
688
- # Whether to run the backend only. Exclusive with REFLEX_FRONTEND_ONLY.
689
- REFLEX_BACKEND_ONLY: EnvVar[bool] = env_var(False)
690
-
691
- # Whether to run the frontend only. Exclusive with REFLEX_BACKEND_ONLY.
692
- REFLEX_FRONTEND_ONLY: EnvVar[bool] = env_var(False)
693
-
694
- # The port to run the frontend on.
695
- REFLEX_FRONTEND_PORT: EnvVar[int | None] = env_var(None)
696
-
697
- # The port to run the backend on.
698
- REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None)
699
-
700
- # If this env var is set to "yes", App.compile will be a no-op
701
- REFLEX_SKIP_COMPILE: EnvVar[bool] = env_var(False, internal=True)
702
-
703
- # Whether to run app harness tests in headless mode.
704
- APP_HARNESS_HEADLESS: EnvVar[bool] = env_var(False)
705
-
706
- # Which app harness driver to use.
707
- APP_HARNESS_DRIVER: EnvVar[str] = env_var("Chrome")
708
-
709
- # Arguments to pass to the app harness driver.
710
- APP_HARNESS_DRIVER_ARGS: EnvVar[str] = env_var("")
711
-
712
- # Whether to check for outdated package versions.
713
- REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True)
714
-
715
- # In which performance mode to run the app.
716
- REFLEX_PERF_MODE: EnvVar[PerformanceMode] = env_var(PerformanceMode.WARN)
717
-
718
- # The maximum size of the reflex state in kilobytes.
719
- REFLEX_STATE_SIZE_LIMIT: EnvVar[int] = env_var(1000)
720
-
721
- # Whether to use the turbopack bundler.
722
- REFLEX_USE_TURBOPACK: EnvVar[bool] = env_var(False)
723
-
724
- # Additional paths to include in the hot reload. Separated by a colon.
725
- REFLEX_HOT_RELOAD_INCLUDE_PATHS: EnvVar[list[Path]] = env_var([])
726
-
727
- # Paths to exclude from the hot reload. Takes precedence over include paths. Separated by a colon.
728
- REFLEX_HOT_RELOAD_EXCLUDE_PATHS: EnvVar[list[Path]] = env_var([])
729
-
730
- # Enables different behavior for when the backend would do a cold start if it was inactive.
731
- REFLEX_DOES_BACKEND_COLD_START: EnvVar[bool] = env_var(False)
732
-
733
- # The timeout for the backend to do a cold start in seconds.
734
- REFLEX_BACKEND_COLD_START_TIMEOUT: EnvVar[int] = env_var(10)
735
-
736
- # Used by flexgen to enumerate the pages.
737
- REFLEX_ADD_ALL_ROUTES_ENDPOINT: EnvVar[bool] = env_var(False)
738
-
739
- # The address to bind the HTTP client to. You can set this to "::" to enable IPv6.
740
- REFLEX_HTTP_CLIENT_BIND_ADDRESS: EnvVar[str | None] = env_var(None)
741
-
742
- # Maximum size of the message in the websocket server in bytes.
743
- REFLEX_SOCKET_MAX_HTTP_BUFFER_SIZE: EnvVar[int] = env_var(
744
- constants.POLLING_MAX_HTTP_BUFFER_SIZE
745
- )
746
-
747
- # The interval to send a ping to the websocket server in seconds.
748
- REFLEX_SOCKET_INTERVAL: EnvVar[int] = env_var(constants.Ping.INTERVAL)
749
-
750
- # The timeout to wait for a pong from the websocket server in seconds.
751
- REFLEX_SOCKET_TIMEOUT: EnvVar[int] = env_var(constants.Ping.TIMEOUT)
752
-
753
- # Whether to run Granian in a spawn process. This enables Reflex to pick up on environment variable changes between hot reloads.
754
- REFLEX_STRICT_HOT_RELOAD: EnvVar[bool] = env_var(False)
755
-
756
-
757
- environment = EnvironmentVariables()
758
-
759
-
760
185
  # These vars are not logged because they may contain sensitive information.
761
186
  _sensitive_env_vars = {"DB_URL", "ASYNC_DB_URL", "REDIS_URL"}
762
187
 
@@ -964,9 +389,8 @@ If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
964
389
  self.state_manager_mode == constants.StateManagerMode.REDIS
965
390
  and not self.redis_url
966
391
  ):
967
- raise ConfigError(
968
- f"{self._prefixes[0]}REDIS_URL is required when using the redis state manager."
969
- )
392
+ msg = f"{self._prefixes[0]}REDIS_URL is required when using the redis state manager."
393
+ raise ConfigError(msg)
970
394
 
971
395
  @property
972
396
  def app_module(self) -> ModuleType | None:
@@ -988,9 +412,9 @@ If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
988
412
  Returns:
989
413
  The module name.
990
414
  """
991
- if self.app_module is not None:
992
- return self.app_module.__name__
993
- return ".".join([self.app_name, self.app_name])
415
+ if self.app_module_import is not None:
416
+ return self.app_module_import
417
+ return self.app_name + "." + self.app_name
994
418
 
995
419
  def update_from_env(self) -> dict[str, Any]:
996
420
  """Update the config values based on set environment variables.
@@ -1007,11 +431,11 @@ If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
1007
431
  for key, field in self.__fields__.items():
1008
432
  # The env var name is the key in uppercase.
1009
433
  for prefix in self._prefixes:
1010
- if env_var := os.environ.get(f"{prefix}{key.upper()}"):
434
+ if environment_variable := os.environ.get(f"{prefix}{key.upper()}"):
1011
435
  break
1012
436
  else:
1013
437
  # Default to non-prefixed env var if other are not found.
1014
- if env_var := os.environ.get(key.upper()):
438
+ if environment_variable := os.environ.get(key.upper()):
1015
439
  console.deprecate(
1016
440
  f"Usage of deprecated {key.upper()} env var detected.",
1017
441
  reason=f"Prefer `{self._prefixes[0]}` prefix when setting env vars.",
@@ -1020,21 +444,23 @@ If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
1020
444
  )
1021
445
 
1022
446
  # If the env var is set, override the config value.
1023
- if env_var and env_var.strip():
447
+ if environment_variable and environment_variable.strip():
1024
448
  # Interpret the value.
1025
449
  value = interpret_env_var_value(
1026
- env_var, true_type_for_pydantic_field(field), field.name
450
+ environment_variable,
451
+ true_type_for_pydantic_field(field),
452
+ field.name,
1027
453
  )
1028
454
 
1029
455
  # Set the value.
1030
456
  updated_values[key] = value
1031
457
 
1032
458
  if key.upper() in _sensitive_env_vars:
1033
- env_var = "***"
459
+ environment_variable = "***"
1034
460
 
1035
461
  if value != getattr(self, key):
1036
462
  console.debug(
1037
- f"Overriding config value {key} with env var {key.upper()}={env_var}",
463
+ f"Overriding config value {key} with env var {key.upper()}={environment_variable}",
1038
464
  dedupe=True,
1039
465
  )
1040
466
  return updated_values