reflex 0.7.8__py3-none-any.whl → 0.7.9a1__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 (37) hide show
  1. reflex/.templates/jinja/web/tailwind.config.js.jinja2 +65 -31
  2. reflex/.templates/web/utils/state.js +11 -1
  3. reflex/app.py +185 -79
  4. reflex/app_mixins/lifespan.py +2 -2
  5. reflex/compiler/compiler.py +31 -4
  6. reflex/components/component.py +39 -57
  7. reflex/components/core/upload.py +8 -0
  8. reflex/components/dynamic.py +9 -1
  9. reflex/components/markdown/markdown.py +0 -21
  10. reflex/components/radix/primitives/accordion.py +1 -1
  11. reflex/components/radix/primitives/form.py +1 -1
  12. reflex/components/radix/primitives/progress.py +1 -1
  13. reflex/components/radix/primitives/slider.py +1 -1
  14. reflex/components/radix/themes/color_mode.py +1 -1
  15. reflex/components/radix/themes/color_mode.pyi +1 -1
  16. reflex/components/recharts/recharts.py +2 -2
  17. reflex/components/sonner/toast.py +1 -1
  18. reflex/config.py +4 -7
  19. reflex/constants/base.py +21 -0
  20. reflex/constants/installer.py +6 -6
  21. reflex/custom_components/custom_components.py +67 -64
  22. reflex/event.py +2 -0
  23. reflex/reflex.py +276 -265
  24. reflex/testing.py +30 -24
  25. reflex/utils/codespaces.py +6 -2
  26. reflex/utils/console.py +4 -3
  27. reflex/utils/exec.py +60 -24
  28. reflex/utils/format.py +17 -2
  29. reflex/utils/prerequisites.py +43 -30
  30. reflex/utils/processes.py +6 -6
  31. reflex/utils/types.py +11 -6
  32. reflex/vars/base.py +19 -1
  33. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/METADATA +6 -9
  34. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/RECORD +37 -37
  35. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/WHEEL +0 -0
  36. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/entry_points.txt +0 -0
  37. {reflex-0.7.8.dist-info → reflex-0.7.9a1.dist-info}/licenses/LICENSE +0 -0
@@ -4,6 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from collections.abc import Iterable, Sequence
6
6
  from datetime import datetime
7
+ from inspect import getmodule
7
8
  from pathlib import Path
8
9
  from typing import TYPE_CHECKING
9
10
 
@@ -55,7 +56,7 @@ def _normalize_library_name(lib: str) -> str:
55
56
  """
56
57
  if lib == "react":
57
58
  return "React"
58
- return lib.replace("@", "").replace("/", "_").replace("-", "_")
59
+ return lib.replace("$/", "").replace("@", "").replace("/", "_").replace("-", "_")
59
60
 
60
61
 
61
62
  def _compile_app(app_root: Component) -> str:
@@ -71,9 +72,6 @@ def _compile_app(app_root: Component) -> str:
71
72
 
72
73
  window_libraries = [
73
74
  (_normalize_library_name(name), name) for name in bundled_libraries
74
- ] + [
75
- ("utils_context", f"$/{constants.Dirs.UTILS}/context"),
76
- ("utils_state", f"$/{constants.Dirs.UTILS}/state"),
77
75
  ]
78
76
 
79
77
  return templates.APP_ROOT.render(
@@ -676,6 +674,35 @@ def _into_component_once(
676
674
  return None
677
675
 
678
676
 
677
+ def readable_name_from_component(
678
+ component: Component | ComponentCallable,
679
+ ) -> str | None:
680
+ """Get the readable name of a component.
681
+
682
+ Args:
683
+ component: The component to get the name of.
684
+
685
+ Returns:
686
+ The readable name of the component.
687
+ """
688
+ if isinstance(component, Component):
689
+ return type(component).__name__
690
+ if isinstance(component, (Var, int, float, str)):
691
+ return str(component)
692
+ if isinstance(component, Sequence):
693
+ return ", ".join(str(c) for c in component)
694
+ if callable(component):
695
+ module_name = getattr(component, "__module__", None)
696
+ if module_name is not None:
697
+ module = getmodule(component)
698
+ if module is not None:
699
+ module_name = module.__name__
700
+ if module_name is not None:
701
+ return f"{module_name}.{component.__name__}"
702
+ return component.__name__
703
+ return None
704
+
705
+
679
706
  def into_component(component: Component | ComponentCallable) -> Component:
680
707
  """Convert a component to a Component.
681
708
 
@@ -1647,32 +1647,6 @@ class Component(BaseComponent, ABC):
1647
1647
 
1648
1648
  return refs
1649
1649
 
1650
- def _get_all_custom_components(
1651
- self, seen: set[str] | None = None
1652
- ) -> set[CustomComponent]:
1653
- """Get all the custom components used by the component.
1654
-
1655
- Args:
1656
- seen: The tags of the components that have already been seen.
1657
-
1658
- Returns:
1659
- The set of custom components.
1660
- """
1661
- custom_components = set()
1662
-
1663
- # Store the seen components in a set to avoid infinite recursion.
1664
- if seen is None:
1665
- seen = set()
1666
- for child in self.children:
1667
- # Skip BaseComponent and StatefulComponent children.
1668
- if not isinstance(child, Component):
1669
- continue
1670
- custom_components |= child._get_all_custom_components(seen=seen)
1671
- for component in self._get_components_in_props():
1672
- if isinstance(component, Component) and component.tag is not None:
1673
- custom_components |= component._get_all_custom_components(seen=seen)
1674
- return custom_components
1675
-
1676
1650
  @property
1677
1651
  def import_var(self):
1678
1652
  """The tag to import.
@@ -1857,37 +1831,6 @@ class CustomComponent(Component):
1857
1831
  """
1858
1832
  return set()
1859
1833
 
1860
- def _get_all_custom_components(
1861
- self, seen: set[str] | None = None
1862
- ) -> set[CustomComponent]:
1863
- """Get all the custom components used by the component.
1864
-
1865
- Args:
1866
- seen: The tags of the components that have already been seen.
1867
-
1868
- Raises:
1869
- ValueError: If the tag is not set.
1870
-
1871
- Returns:
1872
- The set of custom components.
1873
- """
1874
- if self.tag is None:
1875
- raise ValueError("The tag must be set.")
1876
-
1877
- # Store the seen components in a set to avoid infinite recursion.
1878
- if seen is None:
1879
- seen = set()
1880
- custom_components = {self} | super()._get_all_custom_components(seen=seen)
1881
-
1882
- # Avoid adding the same component twice.
1883
- if self.tag not in seen:
1884
- seen.add(self.tag)
1885
- custom_components |= self.get_component(self)._get_all_custom_components(
1886
- seen=seen
1887
- )
1888
-
1889
- return custom_components
1890
-
1891
1834
  @staticmethod
1892
1835
  def _get_event_spec_from_args_spec(name: str, event: EventChain) -> Callable:
1893
1836
  """Get the event spec from the args spec.
@@ -1951,6 +1894,42 @@ class CustomComponent(Component):
1951
1894
  return self.component_fn(*self.get_prop_vars())
1952
1895
 
1953
1896
 
1897
+ CUSTOM_COMPONENTS: dict[str, CustomComponent] = {}
1898
+
1899
+
1900
+ def _register_custom_component(
1901
+ component_fn: Callable[..., Component],
1902
+ ):
1903
+ """Register a custom component to be compiled.
1904
+
1905
+ Args:
1906
+ component_fn: The function that creates the component.
1907
+
1908
+ Raises:
1909
+ TypeError: If the tag name cannot be determined.
1910
+ """
1911
+ dummy_props = {
1912
+ prop: (
1913
+ Var(
1914
+ "",
1915
+ _var_type=annotation,
1916
+ )
1917
+ if not types.safe_issubclass(annotation, EventHandler)
1918
+ else EventSpec(handler=EventHandler(fn=lambda: []))
1919
+ )
1920
+ for prop, annotation in typing.get_type_hints(component_fn).items()
1921
+ if prop != "return"
1922
+ }
1923
+ dummy_component = CustomComponent._create(
1924
+ children=[],
1925
+ component_fn=component_fn,
1926
+ **dummy_props,
1927
+ )
1928
+ if dummy_component.tag is None:
1929
+ raise TypeError(f"Could not determine the tag name for {component_fn!r}")
1930
+ CUSTOM_COMPONENTS[dummy_component.tag] = dummy_component
1931
+
1932
+
1954
1933
  def custom_component(
1955
1934
  component_fn: Callable[..., Component],
1956
1935
  ) -> Callable[..., CustomComponent]:
@@ -1971,6 +1950,9 @@ def custom_component(
1971
1950
  children=list(children), component_fn=component_fn, **props
1972
1951
  )
1973
1952
 
1953
+ # Register this component so it can be compiled.
1954
+ _register_custom_component(component_fn)
1955
+
1974
1956
  return wrapper
1975
1957
 
1976
1958
 
@@ -273,6 +273,14 @@ class Upload(MemoizationLeaf):
273
273
  elif isinstance(on_drop, Callable):
274
274
  # Call the lambda to get the event chain.
275
275
  on_drop = call_event_fn(on_drop, _on_drop_spec)
276
+ if isinstance(on_drop, EventSpec):
277
+ # Update the provided args for direct use with on_drop.
278
+ on_drop = on_drop.with_args(
279
+ args=tuple(
280
+ cls._update_arg_tuple_for_on_drop(arg_value)
281
+ for arg_value in on_drop.args
282
+ ),
283
+ )
276
284
  upload_props["on_drop"] = on_drop
277
285
 
278
286
  input_props_unique_name = get_unique_variable_name()
@@ -26,7 +26,15 @@ def get_cdn_url(lib: str) -> str:
26
26
  return f"https://cdn.jsdelivr.net/npm/{lib}" + "/+esm"
27
27
 
28
28
 
29
- bundled_libraries = {"react", "@radix-ui/themes", "@emotion/react", "next/link"}
29
+ bundled_libraries = {
30
+ "react",
31
+ "@radix-ui/themes",
32
+ "@emotion/react",
33
+ "next/link",
34
+ f"$/{constants.Dirs.UTILS}/context",
35
+ f"$/{constants.Dirs.UTILS}/state",
36
+ f"$/{constants.Dirs.UTILS}/components",
37
+ }
30
38
 
31
39
 
32
40
  def bundle_library(component: Union["Component", str]):
@@ -192,27 +192,6 @@ class Markdown(Component):
192
192
  **props,
193
193
  )
194
194
 
195
- def _get_all_custom_components(
196
- self, seen: set[str] | None = None
197
- ) -> set[CustomComponent]:
198
- """Get all the custom components used by the component.
199
-
200
- Args:
201
- seen: The tags of the components that have already been seen.
202
-
203
- Returns:
204
- The set of custom components.
205
- """
206
- custom_components = super()._get_all_custom_components(seen=seen)
207
-
208
- # Get the custom components for each tag.
209
- for component in self.component_map.values():
210
- custom_components |= component(_MOCK_ARG)._get_all_custom_components(
211
- seen=seen
212
- )
213
-
214
- return custom_components
215
-
216
195
  def add_imports(self) -> ImportDict | list[ImportDict]:
217
196
  """Add imports for the markdown component.
218
197
 
@@ -54,7 +54,7 @@ def _inherited_variant_selector(
54
54
  class AccordionComponent(RadixPrimitiveComponent):
55
55
  """Base class for all @radix-ui/accordion components."""
56
56
 
57
- library = "@radix-ui/react-accordion@^1.2.3"
57
+ library = "@radix-ui/react-accordion@^1.2.8"
58
58
 
59
59
  # The color scheme of the component.
60
60
  color_scheme: Var[LiteralAccentColor]
@@ -17,7 +17,7 @@ from .base import RadixPrimitiveComponentWithClassName
17
17
  class FormComponent(RadixPrimitiveComponentWithClassName):
18
18
  """Base class for all @radix-ui/react-form components."""
19
19
 
20
- library = "@radix-ui/react-form@^0.1.2"
20
+ library = "@radix-ui/react-form@^0.1.4"
21
21
 
22
22
 
23
23
  class FormRoot(FormComponent, HTMLForm):
@@ -15,7 +15,7 @@ from reflex.vars.base import Var
15
15
  class ProgressComponent(RadixPrimitiveComponentWithClassName):
16
16
  """A Progress component."""
17
17
 
18
- library = "@radix-ui/react-progress@^1.1.2"
18
+ library = "@radix-ui/react-progress@^1.1.4"
19
19
 
20
20
 
21
21
  class ProgressRoot(ProgressComponent):
@@ -17,7 +17,7 @@ LiteralSliderDir = Literal["ltr", "rtl"]
17
17
  class SliderComponent(RadixPrimitiveComponentWithClassName):
18
18
  """Base class for all @radix-ui/react-slider components."""
19
19
 
20
- library = "@radix-ui/react-slider@^1.2.3"
20
+ library = "@radix-ui/react-slider@^1.3.2"
21
21
 
22
22
 
23
23
  def on_value_event_spec(
@@ -205,5 +205,5 @@ class ColorModeNamespace(Var):
205
205
  color_mode = color_mode_var_and_namespace = ColorModeNamespace(
206
206
  _js_expr=color_mode._js_expr,
207
207
  _var_type=color_mode._var_type,
208
- _var_data=color_mode._get_default_value(),
208
+ _var_data=color_mode._get_all_var_data(),
209
209
  )
@@ -572,5 +572,5 @@ class ColorModeNamespace(Var):
572
572
  color_mode = color_mode_var_and_namespace = ColorModeNamespace(
573
573
  _js_expr=color_mode._js_expr,
574
574
  _var_type=color_mode._var_type,
575
- _var_data=color_mode._get_default_value(),
575
+ _var_data=color_mode._get_all_var_data(),
576
576
  )
@@ -8,7 +8,7 @@ from reflex.components.component import Component, MemoizationLeaf, NoSSRCompone
8
8
  class Recharts(Component):
9
9
  """A component that wraps a recharts lib."""
10
10
 
11
- library = "recharts@2.15.1"
11
+ library = "recharts@2.15.3"
12
12
 
13
13
  def _get_style(self) -> dict:
14
14
  return {"wrapperStyle": self.style}
@@ -17,7 +17,7 @@ class Recharts(Component):
17
17
  class RechartsCharts(NoSSRComponent, MemoizationLeaf):
18
18
  """A component that wraps a recharts lib."""
19
19
 
20
- library = "recharts@2.15.1"
20
+ library = "recharts@2.15.3"
21
21
 
22
22
 
23
23
  LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"]
@@ -172,7 +172,7 @@ class ToastProps(PropsBase, NoExtrasAllowedProps):
172
172
  class Toaster(Component):
173
173
  """A Toaster Component for displaying toast notifications."""
174
174
 
175
- library: str | None = "sonner@2.0.1"
175
+ library: str | None = "sonner@2.0.3"
176
176
 
177
177
  tag = "Toaster"
178
178
 
reflex/config.py CHANGED
@@ -392,7 +392,7 @@ class EnvVar(Generic[T]):
392
392
  The environment variable value.
393
393
  """
394
394
  env_value = os.getenv(self.name, None)
395
- if env_value is not None:
395
+ if env_value and env_value.strip():
396
396
  return self.interpret(env_value)
397
397
  return None
398
398
 
@@ -402,7 +402,7 @@ class EnvVar(Generic[T]):
402
402
  Returns:
403
403
  True if the environment variable is set.
404
404
  """
405
- return self.name in os.environ
405
+ return bool(os.getenv(self.name, "").strip())
406
406
 
407
407
  def get(self) -> T:
408
408
  """Get the interpreted environment variable value or the default value if not set.
@@ -695,9 +695,6 @@ class EnvironmentVariables:
695
695
  # The port to run the backend on.
696
696
  REFLEX_BACKEND_PORT: EnvVar[int | None] = env_var(None)
697
697
 
698
- # Reflex internal env to reload the config.
699
- RELOAD_CONFIG: EnvVar[bool] = env_var(False, internal=True)
700
-
701
698
  # If this env var is set to "yes", App.compile will be a no-op
702
699
  REFLEX_SKIP_COMPILE: EnvVar[bool] = env_var(False, internal=True)
703
700
 
@@ -906,7 +903,7 @@ class Config(Base):
906
903
  # Set the log level for this process
907
904
  env_loglevel = os.environ.get("LOGLEVEL")
908
905
  if env_loglevel is not None:
909
- env_loglevel = LogLevel(env_loglevel)
906
+ env_loglevel = LogLevel(env_loglevel.lower())
910
907
  if env_loglevel or self.loglevel != LogLevel.DEFAULT:
911
908
  console.set_log_level(env_loglevel or self.loglevel)
912
909
 
@@ -969,7 +966,7 @@ class Config(Base):
969
966
  env_var = os.environ.get(key.upper())
970
967
 
971
968
  # If the env var is set, override the config value.
972
- if env_var is not None:
969
+ if env_var and env_var.strip():
973
970
  # Interpret the value.
974
971
  value = interpret_env_var_value(
975
972
  env_var, true_type_for_pydantic_field(field), field.name
reflex/constants/base.py CHANGED
@@ -7,6 +7,7 @@ from enum import Enum
7
7
  from importlib import metadata
8
8
  from pathlib import Path
9
9
  from types import SimpleNamespace
10
+ from typing import Literal
10
11
 
11
12
  from platformdirs import PlatformDirs
12
13
 
@@ -219,6 +220,9 @@ class ColorMode(SimpleNamespace):
219
220
  SET = "setColorMode"
220
221
 
221
222
 
223
+ LITERAL_ENV = Literal["dev", "prod"]
224
+
225
+
222
226
  # Env modes
223
227
  class Env(str, Enum):
224
228
  """The environment modes."""
@@ -238,6 +242,23 @@ class LogLevel(str, Enum):
238
242
  ERROR = "error"
239
243
  CRITICAL = "critical"
240
244
 
245
+ @classmethod
246
+ def from_string(cls, level: str | None) -> LogLevel | None:
247
+ """Convert a string to a log level.
248
+
249
+ Args:
250
+ level: The log level as a string.
251
+
252
+ Returns:
253
+ The log level.
254
+ """
255
+ if not level:
256
+ return None
257
+ try:
258
+ return LogLevel[level.upper()]
259
+ except KeyError:
260
+ return None
261
+
241
262
  def __le__(self, other: LogLevel) -> bool:
242
263
  """Compare log levels.
243
264
 
@@ -14,7 +14,7 @@ class Bun(SimpleNamespace):
14
14
  """Bun constants."""
15
15
 
16
16
  # The Bun version.
17
- VERSION = "1.2.8"
17
+ VERSION = "1.2.10"
18
18
 
19
19
  # Min Bun Version
20
20
  MIN_VERSION = "1.2.8"
@@ -75,7 +75,7 @@ fetch-retries=0
75
75
 
76
76
 
77
77
  def _determine_nextjs_version() -> str:
78
- default_version = "15.3.0"
78
+ default_version = "15.3.1"
79
79
  if (version := os.getenv("NEXTJS_VERSION")) and version != default_version:
80
80
  from reflex.utils import console
81
81
 
@@ -101,13 +101,13 @@ class PackageJson(SimpleNamespace):
101
101
 
102
102
  DEPENDENCIES = {
103
103
  "@emotion/react": "11.14.0",
104
- "axios": "1.8.3",
104
+ "axios": "1.8.4",
105
105
  "json5": "2.2.3",
106
106
  "next": _determine_nextjs_version(),
107
107
  "next-sitemap": "4.2.3",
108
108
  "next-themes": "0.4.6",
109
- "react": "19.0.0",
110
- "react-dom": "19.0.0",
109
+ "react": "19.1.0",
110
+ "react-dom": "19.1.0",
111
111
  "react-focus-lock": "2.13.6",
112
112
  "socket.io-client": "4.8.1",
113
113
  "universal-cookie": "7.2.2",
@@ -119,5 +119,5 @@ class PackageJson(SimpleNamespace):
119
119
  }
120
120
  OVERRIDES = {
121
121
  # This should always match the `react` version in DEPENDENCIES for recharts compatibility.
122
- "react-is": "19.0.0"
122
+ "react-is": "19.1.0"
123
123
  }