reflex 0.7.14a5__py3-none-any.whl → 0.8.0__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 (236) hide show
  1. reflex/.templates/jinja/app/rxconfig.py.jinja2 +4 -1
  2. reflex/.templates/jinja/web/package.json.jinja2 +1 -1
  3. reflex/.templates/jinja/web/pages/_app.js.jinja2 +21 -11
  4. reflex/.templates/jinja/web/pages/_document.js.jinja2 +1 -1
  5. reflex/.templates/jinja/web/pages/base_page.js.jinja2 +0 -1
  6. reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -0
  7. reflex/.templates/jinja/web/styles/styles.css.jinja2 +1 -0
  8. reflex/.templates/jinja/web/utils/context.js.jinja2 +25 -8
  9. reflex/.templates/web/app/entry.client.js +8 -0
  10. reflex/.templates/web/app/routes.js +10 -0
  11. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +12 -37
  12. reflex/.templates/web/postcss.config.js +1 -1
  13. reflex/.templates/web/react-router.config.js +6 -0
  14. reflex/.templates/web/styles/__reflex_style_reset.css +399 -0
  15. reflex/.templates/web/utils/client_side_routing.js +21 -19
  16. reflex/.templates/web/utils/react-theme.js +92 -0
  17. reflex/.templates/web/utils/state.js +251 -100
  18. reflex/.templates/web/vite-plugin-safari-cachebust.js +160 -0
  19. reflex/.templates/web/vite.config.js +39 -0
  20. reflex/__init__.py +1 -6
  21. reflex/__init__.pyi +327 -192
  22. reflex/app.py +103 -152
  23. reflex/base.py +1 -87
  24. reflex/compiler/compiler.py +70 -19
  25. reflex/compiler/templates.py +3 -3
  26. reflex/compiler/utils.py +91 -33
  27. reflex/components/__init__.py +0 -2
  28. reflex/components/__init__.pyi +34 -18
  29. reflex/components/base/__init__.py +1 -5
  30. reflex/components/base/__init__.pyi +30 -21
  31. reflex/components/base/app_wrap.pyi +7 -7
  32. reflex/components/base/body.pyi +7 -7
  33. reflex/components/base/document.py +18 -14
  34. reflex/components/base/document.pyi +88 -38
  35. reflex/components/base/error_boundary.pyi +7 -7
  36. reflex/components/base/fragment.pyi +7 -7
  37. reflex/components/base/link.pyi +12 -12
  38. reflex/components/base/meta.py +4 -15
  39. reflex/components/base/meta.pyi +31 -31
  40. reflex/components/base/script.py +60 -58
  41. reflex/components/base/script.pyi +248 -34
  42. reflex/components/base/strict_mode.pyi +7 -7
  43. reflex/components/component.py +146 -217
  44. reflex/components/core/__init__.py +1 -0
  45. reflex/components/core/__init__.pyi +77 -37
  46. reflex/components/core/auto_scroll.pyi +7 -7
  47. reflex/components/core/banner.pyi +33 -33
  48. reflex/components/core/client_side_routing.py +7 -6
  49. reflex/components/core/client_side_routing.pyi +8 -59
  50. reflex/components/core/clipboard.pyi +7 -7
  51. reflex/components/core/debounce.py +1 -0
  52. reflex/components/core/debounce.pyi +7 -7
  53. reflex/components/core/foreach.py +5 -4
  54. reflex/components/core/helmet.py +14 -0
  55. reflex/components/{next/base.pyi → core/helmet.pyi} +12 -10
  56. reflex/components/core/html.pyi +7 -7
  57. reflex/components/core/match.py +3 -3
  58. reflex/components/core/sticky.pyi +21 -20
  59. reflex/components/core/upload.py +4 -2
  60. reflex/components/core/upload.pyi +26 -25
  61. reflex/components/datadisplay/__init__.pyi +13 -7
  62. reflex/components/datadisplay/code.py +14 -79
  63. reflex/components/datadisplay/code.pyi +11 -13
  64. reflex/components/datadisplay/dataeditor.pyi +38 -15
  65. reflex/components/datadisplay/shiki_code_block.py +5 -3
  66. reflex/components/datadisplay/shiki_code_block.pyi +16 -15
  67. reflex/components/dynamic.py +5 -5
  68. reflex/components/el/__init__.pyi +506 -246
  69. reflex/components/el/element.pyi +7 -7
  70. reflex/components/el/elements/__init__.pyi +504 -245
  71. reflex/components/el/elements/base.pyi +7 -7
  72. reflex/components/el/elements/forms.pyi +146 -101
  73. reflex/components/el/elements/inline.pyi +142 -142
  74. reflex/components/el/elements/media.pyi +131 -130
  75. reflex/components/el/elements/metadata.pyi +32 -32
  76. reflex/components/el/elements/other.pyi +37 -37
  77. reflex/components/el/elements/scripts.pyi +17 -17
  78. reflex/components/el/elements/sectioning.pyi +77 -77
  79. reflex/components/el/elements/tables.pyi +52 -52
  80. reflex/components/el/elements/typography.pyi +77 -77
  81. reflex/components/field.py +175 -0
  82. reflex/components/gridjs/datatable.py +2 -2
  83. reflex/components/gridjs/datatable.pyi +14 -14
  84. reflex/components/lucide/icon.py +6 -2
  85. reflex/components/lucide/icon.pyi +19 -17
  86. reflex/components/markdown/markdown.py +5 -3
  87. reflex/components/markdown/markdown.pyi +7 -7
  88. reflex/components/moment/moment.py +1 -1
  89. reflex/components/moment/moment.pyi +7 -7
  90. reflex/components/plotly/plotly.py +12 -6
  91. reflex/components/plotly/plotly.pyi +50 -49
  92. reflex/components/props.py +376 -27
  93. reflex/components/radix/__init__.pyi +123 -65
  94. reflex/components/radix/primitives/__init__.pyi +6 -4
  95. reflex/components/radix/primitives/accordion.py +8 -1
  96. reflex/components/radix/primitives/accordion.pyi +37 -37
  97. reflex/components/radix/primitives/base.pyi +12 -12
  98. reflex/components/radix/primitives/drawer.pyi +56 -55
  99. reflex/components/radix/primitives/form.pyi +63 -53
  100. reflex/components/radix/primitives/progress.pyi +26 -25
  101. reflex/components/radix/primitives/slider.pyi +27 -27
  102. reflex/components/radix/themes/__init__.pyi +5 -6
  103. reflex/components/radix/themes/base.py +3 -3
  104. reflex/components/radix/themes/base.pyi +42 -42
  105. reflex/components/radix/themes/color_mode.py +5 -6
  106. reflex/components/radix/themes/color_mode.pyi +17 -17
  107. reflex/components/radix/themes/components/__init__.pyi +75 -38
  108. reflex/components/radix/themes/components/alert_dialog.pyi +37 -37
  109. reflex/components/radix/themes/components/aspect_ratio.pyi +7 -7
  110. reflex/components/radix/themes/components/avatar.pyi +7 -7
  111. reflex/components/radix/themes/components/badge.pyi +7 -7
  112. reflex/components/radix/themes/components/button.pyi +7 -7
  113. reflex/components/radix/themes/components/callout.pyi +26 -25
  114. reflex/components/radix/themes/components/card.pyi +7 -7
  115. reflex/components/radix/themes/components/checkbox.pyi +16 -15
  116. reflex/components/radix/themes/components/checkbox_cards.pyi +12 -12
  117. reflex/components/radix/themes/components/checkbox_group.pyi +12 -12
  118. reflex/components/radix/themes/components/context_menu.pyi +67 -67
  119. reflex/components/radix/themes/components/data_list.pyi +22 -22
  120. reflex/components/radix/themes/components/dialog.pyi +36 -35
  121. reflex/components/radix/themes/components/dropdown_menu.pyi +42 -42
  122. reflex/components/radix/themes/components/hover_card.pyi +21 -20
  123. reflex/components/radix/themes/components/icon_button.pyi +7 -7
  124. reflex/components/radix/themes/components/inset.pyi +7 -7
  125. reflex/components/radix/themes/components/popover.pyi +22 -22
  126. reflex/components/radix/themes/components/progress.pyi +7 -7
  127. reflex/components/radix/themes/components/radio.pyi +7 -7
  128. reflex/components/radix/themes/components/radio_cards.pyi +12 -12
  129. reflex/components/radix/themes/components/radio_group.pyi +21 -20
  130. reflex/components/radix/themes/components/scroll_area.pyi +7 -7
  131. reflex/components/radix/themes/components/segmented_control.pyi +12 -12
  132. reflex/components/radix/themes/components/select.pyi +46 -45
  133. reflex/components/radix/themes/components/separator.pyi +7 -7
  134. reflex/components/radix/themes/components/skeleton.pyi +7 -7
  135. reflex/components/radix/themes/components/slider.pyi +17 -9
  136. reflex/components/radix/themes/components/spinner.pyi +7 -7
  137. reflex/components/radix/themes/components/switch.pyi +7 -7
  138. reflex/components/radix/themes/components/table.pyi +37 -37
  139. reflex/components/radix/themes/components/tabs.pyi +26 -25
  140. reflex/components/radix/themes/components/text_area.pyi +15 -9
  141. reflex/components/radix/themes/components/text_field.pyi +32 -19
  142. reflex/components/radix/themes/components/tooltip.pyi +7 -7
  143. reflex/components/radix/themes/layout/__init__.pyi +27 -14
  144. reflex/components/radix/themes/layout/base.pyi +7 -7
  145. reflex/components/radix/themes/layout/box.pyi +7 -7
  146. reflex/components/radix/themes/layout/center.pyi +7 -7
  147. reflex/components/radix/themes/layout/container.pyi +7 -7
  148. reflex/components/radix/themes/layout/flex.pyi +7 -7
  149. reflex/components/radix/themes/layout/grid.pyi +7 -7
  150. reflex/components/radix/themes/layout/list.pyi +26 -25
  151. reflex/components/radix/themes/layout/section.pyi +7 -7
  152. reflex/components/radix/themes/layout/spacer.pyi +7 -7
  153. reflex/components/radix/themes/layout/stack.pyi +17 -17
  154. reflex/components/radix/themes/typography/__init__.pyi +7 -5
  155. reflex/components/radix/themes/typography/blockquote.pyi +7 -7
  156. reflex/components/radix/themes/typography/code.pyi +7 -7
  157. reflex/components/radix/themes/typography/heading.pyi +7 -7
  158. reflex/components/radix/themes/typography/link.py +46 -11
  159. reflex/components/radix/themes/typography/link.pyi +312 -9
  160. reflex/components/radix/themes/typography/text.pyi +36 -35
  161. reflex/components/react_player/audio.pyi +10 -8
  162. reflex/components/react_player/react_player.pyi +7 -7
  163. reflex/components/react_player/video.pyi +10 -8
  164. reflex/components/recharts/__init__.pyi +208 -100
  165. reflex/components/recharts/cartesian.py +10 -8
  166. reflex/components/recharts/cartesian.pyi +90 -94
  167. reflex/components/recharts/charts.py +4 -2
  168. reflex/components/recharts/charts.pyi +49 -49
  169. reflex/components/recharts/general.pyi +31 -31
  170. reflex/components/recharts/polar.py +8 -4
  171. reflex/components/recharts/polar.pyi +23 -23
  172. reflex/components/recharts/recharts.py +2 -2
  173. reflex/components/recharts/recharts.pyi +12 -12
  174. reflex/components/sonner/toast.py +3 -3
  175. reflex/components/sonner/toast.pyi +9 -9
  176. reflex/config.py +10 -113
  177. reflex/constants/__init__.py +2 -2
  178. reflex/constants/base.py +28 -11
  179. reflex/constants/compiler.py +12 -3
  180. reflex/constants/event.py +1 -0
  181. reflex/constants/installer.py +26 -20
  182. reflex/constants/route.py +27 -8
  183. reflex/constants/state.py +2 -0
  184. reflex/custom_components/custom_components.py +0 -14
  185. reflex/environment.py +77 -5
  186. reflex/event.py +178 -81
  187. reflex/experimental/__init__.py +0 -30
  188. reflex/istate/__init__.py +69 -0
  189. reflex/istate/manager.py +1 -0
  190. reflex/istate/proxy.py +5 -3
  191. reflex/page.py +0 -27
  192. reflex/plugins/__init__.py +3 -2
  193. reflex/plugins/base.py +5 -1
  194. reflex/plugins/shared_tailwind.py +215 -0
  195. reflex/plugins/sitemap.py +206 -0
  196. reflex/plugins/tailwind_v3.py +15 -108
  197. reflex/plugins/tailwind_v4.py +18 -110
  198. reflex/reflex.py +1 -0
  199. reflex/route.py +157 -75
  200. reflex/state.py +171 -155
  201. reflex/testing.py +86 -16
  202. reflex/utils/build.py +38 -82
  203. reflex/utils/exec.py +83 -175
  204. reflex/utils/export.py +2 -2
  205. reflex/utils/format.py +1 -5
  206. reflex/utils/imports.py +5 -16
  207. reflex/utils/misc.py +67 -0
  208. reflex/utils/prerequisites.py +66 -68
  209. reflex/utils/processes.py +24 -47
  210. reflex/utils/pyi_generator.py +44 -49
  211. reflex/utils/serializers.py +14 -1
  212. reflex/utils/telemetry.py +0 -15
  213. reflex/utils/types.py +197 -62
  214. reflex/vars/__init__.py +2 -0
  215. reflex/vars/base.py +367 -134
  216. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/METADATA +15 -8
  217. reflex-0.8.0.dist-info/RECORD +403 -0
  218. reflex/.templates/web/next.config.js +0 -7
  219. reflex/components/base/head.py +0 -20
  220. reflex/components/base/head.pyi +0 -116
  221. reflex/components/next/__init__.py +0 -10
  222. reflex/components/next/base.py +0 -7
  223. reflex/components/next/image.py +0 -117
  224. reflex/components/next/image.pyi +0 -94
  225. reflex/components/next/link.py +0 -20
  226. reflex/components/next/link.pyi +0 -67
  227. reflex/components/next/video.py +0 -38
  228. reflex/components/next/video.pyi +0 -68
  229. reflex/components/suneditor/__init__.py +0 -5
  230. reflex/components/suneditor/editor.py +0 -269
  231. reflex/components/suneditor/editor.pyi +0 -199
  232. reflex/experimental/layout.py +0 -254
  233. reflex-0.7.14a5.dist-info/RECORD +0 -407
  234. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/WHEEL +0 -0
  235. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/entry_points.txt +0 -0
  236. {reflex-0.7.14a5.dist-info → reflex-0.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -14,10 +14,10 @@ class Bun(SimpleNamespace):
14
14
  """Bun constants."""
15
15
 
16
16
  # The Bun version.
17
- VERSION = "1.2.15"
17
+ VERSION = "1.2.17"
18
18
 
19
19
  # Min Bun Version
20
- MIN_VERSION = "1.2.8"
20
+ MIN_VERSION = "1.2.17"
21
21
 
22
22
  # URL to bun install script.
23
23
  INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh"
@@ -63,7 +63,7 @@ class Node(SimpleNamespace):
63
63
  """Node/ NPM constants."""
64
64
 
65
65
  # The minimum required node version.
66
- MIN_VERSION = "18.18.0"
66
+ MIN_VERSION = "20.19.0"
67
67
 
68
68
  # Path of the node config file.
69
69
  CONFIG_PATH = ".npmrc"
@@ -74,13 +74,13 @@ fetch-retries=0
74
74
  """
75
75
 
76
76
 
77
- def _determine_nextjs_version() -> str:
78
- default_version = "15.3.2"
79
- if (version := os.getenv("NEXTJS_VERSION")) and version != default_version:
77
+ def _determine_react_router_version() -> str:
78
+ default_version = "7.6.2"
79
+ if (version := os.getenv("REACT_ROUTER_VERSION")) and version != default_version:
80
80
  from reflex.utils import console
81
81
 
82
82
  console.warn(
83
- f"You have requested next@{version} but the supported version is {default_version}, abandon all hope ye who enter here."
83
+ f"You have requested react-router@{version} but the supported version is {default_version}, abandon all hope ye who enter here."
84
84
  )
85
85
  return version
86
86
  return default_version
@@ -104,15 +104,16 @@ class PackageJson(SimpleNamespace):
104
104
  class Commands(SimpleNamespace):
105
105
  """The commands to define in package.json."""
106
106
 
107
- DEV = "next dev {flags}"
108
- EXPORT = "next build {flags}"
109
- EXPORT_SITEMAP = "next build {flags} && next-sitemap"
110
- PROD = "next start"
107
+ DEV = "react-router dev"
108
+ EXPORT = "react-router build"
109
+ PROD = "serve ./build/client"
111
110
 
112
111
  PATH = "package.json"
113
112
 
114
113
  _react_version = _determine_react_version()
115
114
 
115
+ _react_router_version = _determine_react_router_version()
116
+
116
117
  @classproperty
117
118
  @classmethod
118
119
  def DEPENDENCIES(cls) -> dict[str, str]:
@@ -122,25 +123,30 @@ class PackageJson(SimpleNamespace):
122
123
  A dictionary of dependencies with their versions.
123
124
  """
124
125
  return {
125
- "@emotion/react": "11.14.0",
126
- "axios": "1.9.0",
127
126
  "json5": "2.2.3",
128
- "next": _determine_nextjs_version(),
129
- "next-sitemap": "4.2.3",
130
- "next-themes": "0.4.6",
127
+ "react-router": cls._react_router_version,
128
+ "react-router-dom": cls._react_router_version,
129
+ "@react-router/node": cls._react_router_version,
130
+ "serve": "14.2.4",
131
131
  "react": cls._react_version,
132
+ "react-helmet": "6.1.0",
132
133
  "react-dom": cls._react_version,
133
- "react-focus-lock": "2.13.6",
134
+ "isbot": "5.1.28",
134
135
  "socket.io-client": "4.8.1",
135
136
  "universal-cookie": "7.2.2",
136
137
  }
137
138
 
138
139
  DEV_DEPENDENCIES = {
140
+ "@emotion/react": "11.14.0",
139
141
  "autoprefixer": "10.4.21",
140
- "postcss": "8.5.4",
141
- "postcss-import": "16.1.0",
142
+ "postcss": "8.5.6",
143
+ "postcss-import": "16.1.1",
144
+ "@react-router/dev": _react_router_version,
145
+ "@react-router/fs-routes": _react_router_version,
146
+ "rolldown-vite": "7.0.1",
142
147
  }
143
148
  OVERRIDES = {
144
149
  # This should always match the `react` version in DEPENDENCIES for recharts compatibility.
145
- "react-is": _react_version
150
+ "react-is": _react_version,
151
+ "cookie": "1.0.2",
146
152
  }
reflex/constants/route.py CHANGED
@@ -36,16 +36,35 @@ ROUTER_DATA_INCLUDE = {RouteVar.PATH, RouteVar.ORIGIN, RouteVar.QUERY}
36
36
  class RouteRegex(SimpleNamespace):
37
37
  """Regex used for extracting route args in route."""
38
38
 
39
- ARG = re.compile(r"\[(?!\.)([^\[\]]+)\]")
40
- # group return the catchall pattern (i.e. "[[..slug]]")
41
- CATCHALL = re.compile(r"(\[?\[\.{3}(?![0-9]).*\]?\])")
42
- # group return the arg name (i.e. "slug")
43
- STRICT_CATCHALL = re.compile(r"\[\.{3}([a-zA-Z_][\w]*)\]")
44
- # group return the arg name (i.e. "slug") (optional arg can be empty)
45
- OPT_CATCHALL = re.compile(r"\[\[\.{3}([a-zA-Z_][\w]*)\]\]")
39
+ _DOT_DOT_DOT = r"\.\.\."
40
+ _OPENING_BRACKET = r"\["
41
+ _CLOSING_BRACKET = r"\]"
42
+ _ARG_NAME = r"[a-zA-Z_]\w*"
43
+
44
+ # The regex for a valid arg name, e.g. "slug" in "[slug]"
45
+ _ARG_NAME_PATTERN = re.compile(_ARG_NAME)
46
+
47
+ SLUG = re.compile(r"[a-zA-Z0-9_-]+")
48
+ # match a single arg (i.e. "[slug]"), returns the name of the arg
49
+ ARG = re.compile(rf"{_OPENING_BRACKET}({_ARG_NAME}){_CLOSING_BRACKET}")
50
+ # match a single optional arg (i.e. "[[slug]]"), returns the name of the arg
51
+ OPTIONAL_ARG = re.compile(
52
+ rf"{_OPENING_BRACKET * 2}({_ARG_NAME}){_CLOSING_BRACKET * 2}"
53
+ )
54
+
55
+ # match a single non-optional catch-all arg (i.e. "[...slug]"), returns the name of the arg
56
+ STRICT_CATCHALL = re.compile(
57
+ rf"{_OPENING_BRACKET}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET}"
58
+ )
59
+
60
+ # match a single optional catch-all arg (i.e. "[[...slug]]"), returns the name of the arg
61
+ OPTIONAL_CATCHALL = re.compile(
62
+ rf"{_OPENING_BRACKET * 2}{_DOT_DOT_DOT}({_ARG_NAME}){_CLOSING_BRACKET * 2}"
63
+ )
64
+
65
+ SPLAT_CATCHALL = "[[...splat]]"
46
66
  SINGLE_SEGMENT = "__SINGLE_SEGMENT__"
47
67
  DOUBLE_SEGMENT = "__DOUBLE_SEGMENT__"
48
- SINGLE_CATCHALL_SEGMENT = "__SINGLE_CATCHALL_SEGMENT__"
49
68
  DOUBLE_CATCHALL_SEGMENT = "__DOUBLE_CATCHALL_SEGMENT__"
50
69
 
51
70
 
reflex/constants/state.py CHANGED
@@ -13,3 +13,5 @@ class StateManagerMode(str, Enum):
13
13
 
14
14
  # Used for things like console_log, etc.
15
15
  FRONTEND_EVENT_STATE = "__reflex_internal_frontend_event_state"
16
+
17
+ FIELD_MARKER = "_rx_state_"
@@ -461,20 +461,6 @@ def build():
461
461
  _run_build()
462
462
 
463
463
 
464
- @custom_components_cli.command(name="publish", deprecated=True)
465
- def publish():
466
- """Publish a custom component. This command is deprecated and will be removed in future releases.
467
-
468
- Raises:
469
- Exit: If the publish command fails.
470
- """
471
- console.error(
472
- "The publish command is deprecated. You can use `reflex component build` followed by `twine upload` or a similar publishing command to publish your custom component."
473
- "\nIf you want to share your custom component with the Reflex community, please use `reflex component share`."
474
- )
475
- raise click.exceptions.Exit(code=1)
476
-
477
-
478
464
  def _collect_details_for_gallery():
479
465
  """Helper to collect details on the custom component to be included in the gallery.
480
466
 
reflex/environment.py CHANGED
@@ -69,7 +69,7 @@ def interpret_boolean_env(value: str, field_name: str) -> bool:
69
69
  return True
70
70
  if value.lower() in false_values:
71
71
  return False
72
- msg = f"Invalid boolean value: {value} for {field_name}"
72
+ msg = f"Invalid boolean value: {value!r} for {field_name}"
73
73
  raise EnvironmentVarValueError(msg)
74
74
 
75
75
 
@@ -89,7 +89,7 @@ def interpret_int_env(value: str, field_name: str) -> int:
89
89
  try:
90
90
  return int(value)
91
91
  except ValueError as ve:
92
- msg = f"Invalid integer value: {value} for {field_name}"
92
+ msg = f"Invalid integer value: {value!r} for {field_name}"
93
93
  raise EnvironmentVarValueError(msg) from ve
94
94
 
95
95
 
@@ -108,7 +108,7 @@ def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
108
108
  """
109
109
  path = Path(value)
110
110
  if not path.exists():
111
- msg = f"Path does not exist: {path} for {field_name}"
111
+ msg = f"Path does not exist: {path!r} for {field_name}"
112
112
  raise EnvironmentVarValueError(msg)
113
113
  return path
114
114
 
@@ -143,7 +143,7 @@ def interpret_enum_env(value: str, field_type: GenericType, field_name: str) ->
143
143
  try:
144
144
  return field_type(value)
145
145
  except ValueError as ve:
146
- msg = f"Invalid enum value: {value} for {field_name}"
146
+ msg = f"Invalid enum value: {value!r} for {field_name}"
147
147
  raise EnvironmentVarValueError(msg) from ve
148
148
 
149
149
 
@@ -169,6 +169,8 @@ def interpret_env_var_value(
169
169
  msg = f"Union types are not supported for environment variables: {field_name}."
170
170
  raise ValueError(msg)
171
171
 
172
+ value = value.strip()
173
+
172
174
  if field_type is bool:
173
175
  return interpret_boolean_env(value, field_name)
174
176
  if field_type is str:
@@ -475,7 +477,7 @@ class EnvironmentVariables:
475
477
  # Whether to use the system installed bun. If set to false, bun will be bundled with the app.
476
478
  REFLEX_USE_SYSTEM_BUN: EnvVar[bool] = env_var(False)
477
479
 
478
- # The working directory for the next.js commands.
480
+ # The working directory for the frontend directory.
479
481
  REFLEX_WEB_WORKDIR: EnvVar[Path] = env_var(Path(constants.Dirs.WEB))
480
482
 
481
483
  # The working directory for the states directory.
@@ -604,3 +606,73 @@ class EnvironmentVariables:
604
606
 
605
607
 
606
608
  environment = EnvironmentVariables()
609
+
610
+ try:
611
+ from dotenv import load_dotenv
612
+ except ImportError:
613
+ load_dotenv = None
614
+
615
+
616
+ def _paths_from_env_files(env_files: str) -> list[Path]:
617
+ """Convert a string of paths separated by os.pathsep into a list of Path objects.
618
+
619
+ Args:
620
+ env_files: The string of paths.
621
+
622
+ Returns:
623
+ A list of Path objects.
624
+ """
625
+ # load env files in reverse order
626
+ return list(
627
+ reversed(
628
+ [
629
+ Path(path)
630
+ for path_element in env_files.split(os.pathsep)
631
+ if (path := path_element.strip())
632
+ ]
633
+ )
634
+ )
635
+
636
+
637
+ def _load_dotenv_from_files(files: list[Path]):
638
+ """Load environment variables from a list of files.
639
+
640
+ Args:
641
+ files: A list of Path objects representing the environment variable files.
642
+ """
643
+ from reflex.utils import console
644
+
645
+ if not files:
646
+ return
647
+
648
+ if load_dotenv is None:
649
+ console.error(
650
+ """The `python-dotenv` package is required to load environment variables from a file. Run `pip install "python-dotenv>=1.1.0"`."""
651
+ )
652
+ return
653
+
654
+ for env_file in files:
655
+ if env_file.exists():
656
+ load_dotenv(env_file, override=True)
657
+
658
+
659
+ def _paths_from_environment() -> list[Path]:
660
+ """Get the paths from the REFLEX_ENV_FILE environment variable.
661
+
662
+ Returns:
663
+ A list of Path objects.
664
+ """
665
+ env_files = os.environ.get("REFLEX_ENV_FILE")
666
+ if not env_files:
667
+ return []
668
+
669
+ return _paths_from_env_files(env_files)
670
+
671
+
672
+ def _load_dotenv_from_env():
673
+ """Load environment variables from paths specified in REFLEX_ENV_FILE."""
674
+ _load_dotenv_from_files(_paths_from_environment())
675
+
676
+
677
+ # Load the env files at import time if they are set in the ENV_FILE environment variable.
678
+ _load_dotenv_from_env()