reflex 0.7.2.dev1__py3-none-any.whl → 0.7.3__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 (49) hide show
  1. reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +6 -3
  2. reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -1
  3. reflex/.templates/web/components/shiki/code.js +26 -21
  4. reflex/.templates/web/postcss.config.js +1 -1
  5. reflex/.templates/web/utils/client_side_routing.js +18 -16
  6. reflex/.templates/web/utils/helpers/dataeditor.js +1 -1
  7. reflex/.templates/web/utils/helpers/range.js +30 -30
  8. reflex/.templates/web/utils/state.js +44 -22
  9. reflex/app_mixins/middleware.py +9 -10
  10. reflex/compiler/compiler.py +7 -0
  11. reflex/components/core/banner.py +1 -1
  12. reflex/components/core/foreach.py +3 -2
  13. reflex/components/datadisplay/logo.py +1 -1
  14. reflex/components/el/__init__.pyi +22 -0
  15. reflex/components/el/elements/__init__.py +20 -1
  16. reflex/components/el/elements/__init__.pyi +42 -1
  17. reflex/components/el/elements/media.py +11 -0
  18. reflex/components/el/elements/media.pyi +11 -0
  19. reflex/components/lucide/icon.py +8 -6
  20. reflex/components/lucide/icon.pyi +0 -1
  21. reflex/components/radix/themes/components/slider.py +2 -1
  22. reflex/config.py +13 -7
  23. reflex/custom_components/custom_components.py +23 -25
  24. reflex/event.py +7 -2
  25. reflex/istate/data.py +59 -7
  26. reflex/reflex.py +69 -30
  27. reflex/state.py +3 -3
  28. reflex/testing.py +4 -10
  29. reflex/utils/exceptions.py +31 -1
  30. reflex/utils/exec.py +4 -8
  31. reflex/utils/export.py +2 -2
  32. reflex/vars/base.py +23 -1
  33. reflex/vars/number.py +22 -21
  34. reflex/vars/object.py +29 -0
  35. reflex/vars/sequence.py +37 -0
  36. {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/METADATA +42 -42
  37. {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/RECORD +61 -70
  38. {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/WHEEL +1 -1
  39. {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/entry_points.txt +0 -3
  40. benchmarks/__init__.py +0 -3
  41. benchmarks/benchmark_compile_times.py +0 -147
  42. benchmarks/benchmark_imports.py +0 -128
  43. benchmarks/benchmark_lighthouse.py +0 -75
  44. benchmarks/benchmark_package_size.py +0 -135
  45. benchmarks/benchmark_web_size.py +0 -106
  46. benchmarks/conftest.py +0 -20
  47. benchmarks/lighthouse.sh +0 -77
  48. benchmarks/utils.py +0 -74
  49. {reflex-0.7.2.dev1.dist-info → reflex-0.7.3.dist-info}/licenses/LICENSE +0 -0
@@ -89,29 +89,51 @@ from .inline import u as u
89
89
  from .inline import wbr as wbr
90
90
  from .media import Area as Area
91
91
  from .media import Audio as Audio
92
+ from .media import Circle as Circle
93
+ from .media import Defs as Defs
94
+ from .media import Ellipse as Ellipse
92
95
  from .media import Embed as Embed
93
96
  from .media import Iframe as Iframe
94
97
  from .media import Img as Img
98
+ from .media import Line as Line
99
+ from .media import LinearGradient as LinearGradient
95
100
  from .media import Map as Map
96
101
  from .media import Object as Object
102
+ from .media import Path as Path
97
103
  from .media import Picture as Picture
104
+ from .media import Polygon as Polygon
98
105
  from .media import Portal as Portal
106
+ from .media import RadialGradient as RadialGradient
107
+ from .media import Rect as Rect
99
108
  from .media import Source as Source
109
+ from .media import Stop as Stop
100
110
  from .media import Svg as Svg
111
+ from .media import Text as Text
101
112
  from .media import Track as Track
102
113
  from .media import Video as Video
103
114
  from .media import area as area
104
115
  from .media import audio as audio
116
+ from .media import circle as circle
117
+ from .media import defs as defs
118
+ from .media import ellipse as ellipse
105
119
  from .media import embed as embed
106
120
  from .media import iframe as iframe
107
121
  from .media import image as image
108
122
  from .media import img as img
123
+ from .media import line as line
124
+ from .media import linear_gradient as linear_gradient
109
125
  from .media import map as map
110
126
  from .media import object as object
127
+ from .media import path as path
111
128
  from .media import picture as picture
129
+ from .media import polygon as polygon
112
130
  from .media import portal as portal
131
+ from .media import radial_gradient as radial_gradient
132
+ from .media import rect as rect
113
133
  from .media import source as source
134
+ from .media import stop as stop
114
135
  from .media import svg as svg
136
+ from .media import text as text
115
137
  from .media import track as track
116
138
  from .media import video as video
117
139
  from .metadata import Base as Base
@@ -287,6 +309,17 @@ _MAPPING = {
287
309
  "portal",
288
310
  "source",
289
311
  "svg",
312
+ "text",
313
+ "line",
314
+ "circle",
315
+ "ellipse",
316
+ "rect",
317
+ "polygon",
318
+ "path",
319
+ "stop",
320
+ "linear_gradient",
321
+ "radial_gradient",
322
+ "defs",
290
323
  ],
291
324
  "metadata": ["base", "head", "link", "meta", "title", "style"],
292
325
  "other": ["details", "dialog", "summary", "slot", "template", "math", "html"],
@@ -340,4 +373,12 @@ _MAPPING = {
340
373
  }
341
374
  EXCLUDE = ["del_", "Del", "image"]
342
375
  for v in _MAPPING.values():
343
- v.extend([mod.capitalize() for mod in v if mod not in EXCLUDE])
376
+ from reflex.utils.format import to_camel_case
377
+
378
+ v.extend(
379
+ [
380
+ to_camel_case(mod)[0].upper() + to_camel_case(mod)[1:]
381
+ for mod in v
382
+ if mod not in EXCLUDE
383
+ ]
384
+ )
@@ -501,6 +501,17 @@ class SVG(ComponentNamespace):
501
501
  __call__ = staticmethod(Svg.create)
502
502
 
503
503
 
504
+ text = Text.create
505
+ line = Line.create
506
+ circle = Circle.create
507
+ ellipse = Ellipse.create
508
+ rect = Rect.create
509
+ polygon = Polygon.create
510
+ path = Path.create
511
+ stop = Stop.create
512
+ linear_gradient = LinearGradient.create
513
+ radial_gradient = RadialGradient.create
514
+ defs = Defs.create
504
515
  area = Area.create
505
516
  audio = Audio.create
506
517
  image = img = Img.create
@@ -6345,6 +6345,17 @@ class SVG(ComponentNamespace):
6345
6345
  """
6346
6346
  ...
6347
6347
 
6348
+ text = Text.create
6349
+ line = Line.create
6350
+ circle = Circle.create
6351
+ ellipse = Ellipse.create
6352
+ rect = Rect.create
6353
+ polygon = Polygon.create
6354
+ path = Path.create
6355
+ stop = Stop.create
6356
+ linear_gradient = LinearGradient.create
6357
+ radial_gradient = RadialGradient.create
6358
+ defs = Defs.create
6348
6359
  area = Area.create
6349
6360
  audio = Audio.create
6350
6361
  image = img = Img.create
@@ -1,7 +1,7 @@
1
1
  """Lucide Icon component."""
2
2
 
3
3
  from reflex.components.component import Component
4
- from reflex.utils import format
4
+ from reflex.utils import console, format
5
5
  from reflex.utils.imports import ImportVar
6
6
  from reflex.vars.base import LiteralVar, Var
7
7
  from reflex.vars.sequence import LiteralStringVar, StringVar
@@ -33,7 +33,6 @@ class Icon(LucideIconComponent):
33
33
 
34
34
  Raises:
35
35
  AttributeError: The errors tied to bad usage of the Icon component.
36
- ValueError: If the icon tag is invalid.
37
36
  TypeError: If the icon name is not a string.
38
37
 
39
38
  Returns:
@@ -73,15 +72,18 @@ class Icon(LucideIconComponent):
73
72
  if isinstance(tag, str):
74
73
  icons_sorted = sorted(
75
74
  LUCIDE_ICON_LIST,
76
- key=lambda s: format.length_of_largest_common_substring(tag, s),
75
+ key=lambda s, tag=tag: format.length_of_largest_common_substring(
76
+ tag, s
77
+ ),
77
78
  reverse=True,
78
79
  )
79
80
  else:
80
81
  icons_sorted = LUCIDE_ICON_LIST
81
- raise ValueError(
82
- f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(icons_sorted[0:25])}, ..."
83
- "\nSee full list at https://reflex.dev/docs/library/data-display/icon/#icons-list."
82
+ console.warn(
83
+ f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(icons_sorted[0:10])}, ..."
84
+ "\nSee full list at https://reflex.dev/docs/library/data-display/icon/#icons-list. Using 'circle-help' icon instead."
84
85
  )
86
+ tag = "circle-help"
85
87
 
86
88
  if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
87
89
  props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
@@ -103,7 +103,6 @@ class Icon(LucideIconComponent):
103
103
 
104
104
  Raises:
105
105
  AttributeError: The errors tied to bad usage of the Icon component.
106
- ValueError: If the icon tag is invalid.
107
106
  TypeError: If the icon name is not a string.
108
107
 
109
108
  Returns:
@@ -7,6 +7,7 @@ from typing import Literal, Sequence
7
7
  from reflex.components.component import Component
8
8
  from reflex.components.core.breakpoints import Responsive
9
9
  from reflex.event import EventHandler, passthrough_event_spec
10
+ from reflex.utils.types import typehint_issubclass
10
11
  from reflex.vars.base import Var
11
12
 
12
13
  from ..base import LiteralAccentColor, RadixThemesComponent
@@ -96,7 +97,7 @@ class Slider(RadixThemesComponent):
96
97
  width = props.pop("width", "100%")
97
98
 
98
99
  if isinstance(default_value, Var):
99
- if issubclass(default_value._var_type, (int, float)):
100
+ if typehint_issubclass(default_value._var_type, int | float):
100
101
  default_value = [default_value]
101
102
 
102
103
  elif isinstance(default_value, (int, float)):
reflex/config.py CHANGED
@@ -34,6 +34,7 @@ from reflex_cli.constants.hosting import Hosting
34
34
 
35
35
  from reflex import constants
36
36
  from reflex.base import Base
37
+ from reflex.constants.base import LogLevel
37
38
  from reflex.utils import console
38
39
  from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
39
40
  from reflex.utils.types import (
@@ -877,6 +878,13 @@ class Config(Base):
877
878
  """
878
879
  super().__init__(*args, **kwargs)
879
880
 
881
+ # Set the log level for this process
882
+ env_loglevel = os.environ.get("LOGLEVEL")
883
+ if env_loglevel is not None:
884
+ env_loglevel = LogLevel(env_loglevel)
885
+ if env_loglevel or self.loglevel != LogLevel.DEFAULT:
886
+ console.set_log_level(env_loglevel or self.loglevel)
887
+
880
888
  # Update the config from environment variables.
881
889
  env_kwargs = self.update_from_env()
882
890
  for key, env_value in env_kwargs.items():
@@ -887,9 +895,6 @@ class Config(Base):
887
895
  self._non_default_attributes.update(kwargs)
888
896
  self._replace_defaults(**kwargs)
889
897
 
890
- # Set the log level for this process
891
- console.set_log_level(self.loglevel)
892
-
893
898
  if (
894
899
  self.state_manager_mode == constants.StateManagerMode.REDIS
895
900
  and not self.redis_url
@@ -958,10 +963,11 @@ class Config(Base):
958
963
  if key.upper() in _sensitive_env_vars:
959
964
  env_var = "***"
960
965
 
961
- console.info(
962
- f"Overriding config value {key} with env var {key.upper()}={env_var}",
963
- dedupe=True,
964
- )
966
+ if value != getattr(self, key):
967
+ console.info(
968
+ f"Overriding config value {key} with env var {key.upper()}={env_var}",
969
+ dedupe=True,
970
+ )
965
971
 
966
972
  return updated_values
967
973
 
@@ -20,16 +20,8 @@ from reflex.config import environment, get_config
20
20
  from reflex.constants import CustomComponents
21
21
  from reflex.utils import console
22
22
 
23
- config = get_config()
24
23
  custom_components_cli = typer.Typer()
25
24
 
26
- POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT = (
27
- f"{config.cp_backend_url}/custom-components/gallery"
28
- )
29
-
30
- GET_CUSTOM_COMPONENTS_GALLERY_BY_NAME_ENDPOINT = (
31
- f"{config.cp_backend_url}/custom-components/gallery"
32
- )
33
25
 
34
26
  POST_CUSTOM_COMPONENTS_GALLERY_TIMEOUT = 15
35
27
 
@@ -318,8 +310,8 @@ def init(
318
310
  True,
319
311
  help="Whether to install package from this local custom component in editable mode.",
320
312
  ),
321
- loglevel: constants.LogLevel = typer.Option(
322
- config.loglevel, help="The log level to use."
313
+ loglevel: constants.LogLevel | None = typer.Option(
314
+ None, help="The log level to use."
323
315
  ),
324
316
  ):
325
317
  """Initialize a custom component.
@@ -334,7 +326,7 @@ def init(
334
326
  """
335
327
  from reflex.utils import exec, prerequisites
336
328
 
337
- console.set_log_level(loglevel)
329
+ console.set_log_level(loglevel or get_config().loglevel)
338
330
 
339
331
  if CustomComponents.PYPROJECT_TOML.exists():
340
332
  console.error(f"A {CustomComponents.PYPROJECT_TOML} already exists. Aborting.")
@@ -460,8 +452,8 @@ def _run_build():
460
452
 
461
453
  @custom_components_cli.command(name="build")
462
454
  def build(
463
- loglevel: constants.LogLevel = typer.Option(
464
- config.loglevel, help="The log level to use."
455
+ loglevel: constants.LogLevel | None = typer.Option(
456
+ None, help="The log level to use."
465
457
  ),
466
458
  ):
467
459
  """Build a custom component. Must be run from the project root directory where the pyproject.toml is.
@@ -469,7 +461,7 @@ def build(
469
461
  Args:
470
462
  loglevel: The log level to use.
471
463
  """
472
- console.set_log_level(loglevel)
464
+ console.set_log_level(loglevel or get_config().loglevel)
473
465
  _run_build()
474
466
 
475
467
 
@@ -641,8 +633,8 @@ def publish(
641
633
  True,
642
634
  help="Whether to interactively validate the project information in the pyproject.toml file.",
643
635
  ),
644
- loglevel: constants.LogLevel = typer.Option(
645
- config.loglevel, help="The log level to use."
636
+ loglevel: constants.LogLevel | None = typer.Option(
637
+ None, help="The log level to use."
646
638
  ),
647
639
  ):
648
640
  """Publish a custom component. Must be run from the project root directory where the pyproject.toml is.
@@ -660,7 +652,7 @@ def publish(
660
652
  Raises:
661
653
  Exit: If arguments provided are not correct or the publish fails.
662
654
  """
663
- console.set_log_level(loglevel)
655
+ console.set_log_level(loglevel or get_config().loglevel)
664
656
 
665
657
  # Validate the repository name.
666
658
  repository = _validate_repository_name(repository)
@@ -853,6 +845,12 @@ def _collect_details_for_gallery():
853
845
  console.print(f"[ Custom component package name ] : {package_name}")
854
846
  params["package_name"] = package_name
855
847
 
848
+ config = get_config()
849
+
850
+ post_custom_components_gallery_endpoint = (
851
+ f"{config.cp_backend_url}/custom-components/gallery"
852
+ )
853
+
856
854
  # Check the backend services if the user is allowed to update information of this package is already shared.
857
855
  try:
858
856
  console.debug(
@@ -862,7 +860,7 @@ def _collect_details_for_gallery():
862
860
  # 1. Check if the package is already shared by the user. If not, the backend will return 403.
863
861
  # 2. If this package is not shared before, this request records the package name in the backend.
864
862
  response = httpx.post(
865
- POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT,
863
+ post_custom_components_gallery_endpoint,
866
864
  headers={"Authorization": f"Bearer {access_token}"},
867
865
  data=params,
868
866
  )
@@ -899,7 +897,7 @@ def _collect_details_for_gallery():
899
897
  try:
900
898
  console.debug(f"Sending custom component data: {params}")
901
899
  response = httpx.post(
902
- POST_CUSTOM_COMPONENTS_GALLERY_ENDPOINT,
900
+ post_custom_components_gallery_endpoint,
903
901
  headers={"Authorization": f"Bearer {access_token}"},
904
902
  data=params,
905
903
  files=files,
@@ -949,8 +947,8 @@ def _get_file_from_prompt_in_loop() -> tuple[bytes, str] | None:
949
947
 
950
948
  @custom_components_cli.command(name="share")
951
949
  def share_more_detail(
952
- loglevel: constants.LogLevel = typer.Option(
953
- config.loglevel, help="The log level to use."
950
+ loglevel: constants.LogLevel | None = typer.Option(
951
+ None, help="The log level to use."
954
952
  ),
955
953
  ):
956
954
  """Collect more details on the published package for gallery.
@@ -958,15 +956,15 @@ def share_more_detail(
958
956
  Args:
959
957
  loglevel: The log level to use.
960
958
  """
961
- console.set_log_level(loglevel)
959
+ console.set_log_level(loglevel or get_config().loglevel)
962
960
 
963
961
  _collect_details_for_gallery()
964
962
 
965
963
 
966
964
  @custom_components_cli.command()
967
965
  def install(
968
- loglevel: constants.LogLevel = typer.Option(
969
- config.loglevel, help="The log level to use."
966
+ loglevel: constants.LogLevel | None = typer.Option(
967
+ None, help="The log level to use."
970
968
  ),
971
969
  ):
972
970
  """Install package from this local custom component in editable mode.
@@ -977,7 +975,7 @@ def install(
977
975
  Raises:
978
976
  Exit: If unable to install the current directory in editable mode.
979
977
  """
980
- console.set_log_level(loglevel)
978
+ console.set_log_level(loglevel or get_config().loglevel)
981
979
 
982
980
  if _pip_install_on_demand(package_name=".", install_args=["-e"]):
983
981
  console.info("Package installed successfully!")
reflex/event.py CHANGED
@@ -37,7 +37,12 @@ from reflex.utils.exceptions import (
37
37
  EventHandlerArgTypeMismatchError,
38
38
  MissingAnnotationError,
39
39
  )
40
- from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
40
+ from reflex.utils.types import (
41
+ ArgsSpec,
42
+ GenericType,
43
+ safe_issubclass,
44
+ typehint_issubclass,
45
+ )
41
46
  from reflex.vars import VarData
42
47
  from reflex.vars.base import LiteralVar, Var
43
48
  from reflex.vars.function import (
@@ -424,7 +429,7 @@ class EventChain(EventActionsMixin):
424
429
  return value
425
430
  elif isinstance(value, EventVar):
426
431
  value = [value]
427
- elif issubclass(value._var_type, (EventChain, EventSpec)):
432
+ elif safe_issubclass(value._var_type, (EventChain, EventSpec)):
428
433
  return cls.create(
429
434
  value=value.guess_type(),
430
435
  args_spec=args_spec,
reflex/istate/data.py CHANGED
@@ -1,15 +1,32 @@
1
1
  """This module contains the dataclasses representing the router object."""
2
2
 
3
3
  import dataclasses
4
+ from typing import Mapping
4
5
 
5
6
  from reflex import constants
6
7
  from reflex.utils import format
8
+ from reflex.utils.serializers import serializer
7
9
 
8
10
 
9
- @dataclasses.dataclass(frozen=True)
10
- class HeaderData:
11
- """An object containing headers data."""
11
+ @dataclasses.dataclass(frozen=True, init=False)
12
+ class _FrozenDictStrStr(Mapping[str, str]):
13
+ _data: tuple[tuple[str, str], ...]
14
+
15
+ def __init__(self, **kwargs):
16
+ object.__setattr__(self, "_data", tuple(sorted(kwargs.items())))
17
+
18
+ def __getitem__(self, key: str) -> str:
19
+ return dict(self._data)[key]
20
+
21
+ def __iter__(self):
22
+ return (x[0] for x in self._data)
12
23
 
24
+ def __len__(self):
25
+ return len(self._data)
26
+
27
+
28
+ @dataclasses.dataclass(frozen=True)
29
+ class _HeaderData:
13
30
  host: str = ""
14
31
  origin: str = ""
15
32
  upgrade: str = ""
@@ -23,6 +40,14 @@ class HeaderData:
23
40
  sec_websocket_extensions: str = ""
24
41
  accept_encoding: str = ""
25
42
  accept_language: str = ""
43
+ raw_headers: Mapping[str, str] = dataclasses.field(
44
+ default_factory=_FrozenDictStrStr
45
+ )
46
+
47
+
48
+ @dataclasses.dataclass(frozen=True, init=False)
49
+ class HeaderData(_HeaderData):
50
+ """An object containing headers data."""
26
51
 
27
52
  def __init__(self, router_data: dict | None = None):
28
53
  """Initialize the HeaderData object based on router_data.
@@ -30,12 +55,39 @@ class HeaderData:
30
55
  Args:
31
56
  router_data: the router_data dict.
32
57
  """
58
+ super().__init__()
33
59
  if router_data:
60
+ fields_names = [f.name for f in dataclasses.fields(self)]
34
61
  for k, v in router_data.get(constants.RouteVar.HEADERS, {}).items():
35
- object.__setattr__(self, format.to_snake_case(k), v)
36
- else:
37
- for k in dataclasses.fields(self):
38
- object.__setattr__(self, k.name, "")
62
+ snake_case_key = format.to_snake_case(k)
63
+ if snake_case_key in fields_names:
64
+ object.__setattr__(self, snake_case_key, v)
65
+ object.__setattr__(
66
+ self,
67
+ "raw_headers",
68
+ _FrozenDictStrStr(
69
+ **{
70
+ k: v
71
+ for k, v in router_data.get(
72
+ constants.RouteVar.HEADERS, {}
73
+ ).items()
74
+ if v
75
+ }
76
+ ),
77
+ )
78
+
79
+
80
+ @serializer(to=dict)
81
+ def serialize_frozen_dict_str_str(obj: _FrozenDictStrStr) -> dict:
82
+ """Serialize a _FrozenDictStrStr object to a dict.
83
+
84
+ Args:
85
+ obj: the _FrozenDictStrStr object.
86
+
87
+ Returns:
88
+ A dict representation of the _FrozenDictStrStr object.
89
+ """
90
+ return dict(obj._data)
39
91
 
40
92
 
41
93
  @dataclasses.dataclass(frozen=True)