reflex 0.7.2a2__py3-none-any.whl → 0.7.3a1__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.
- benchmarks/__init__.py +3 -0
- benchmarks/benchmark_compile_times.py +147 -0
- benchmarks/benchmark_imports.py +128 -0
- benchmarks/benchmark_lighthouse.py +75 -0
- benchmarks/benchmark_package_size.py +135 -0
- benchmarks/benchmark_web_size.py +106 -0
- benchmarks/conftest.py +20 -0
- benchmarks/lighthouse.sh +77 -0
- benchmarks/utils.py +74 -0
- reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +6 -3
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +1 -1
- reflex/.templates/web/components/shiki/code.js +26 -21
- reflex/.templates/web/postcss.config.js +1 -1
- reflex/.templates/web/utils/client_side_routing.js +18 -16
- reflex/.templates/web/utils/helpers/dataeditor.js +1 -1
- reflex/.templates/web/utils/helpers/range.js +30 -30
- reflex/.templates/web/utils/state.js +44 -22
- reflex/app_mixins/middleware.py +7 -7
- reflex/compiler/compiler.py +7 -0
- reflex/components/core/banner.py +1 -1
- reflex/components/core/foreach.py +3 -2
- reflex/components/datadisplay/logo.py +1 -1
- reflex/components/el/__init__.pyi +22 -0
- reflex/components/el/elements/__init__.py +20 -1
- reflex/components/el/elements/__init__.pyi +42 -1
- reflex/components/el/elements/media.py +11 -0
- reflex/components/el/elements/media.pyi +11 -0
- reflex/components/lucide/icon.py +8 -6
- reflex/components/lucide/icon.pyi +0 -1
- reflex/components/radix/themes/components/slider.py +2 -1
- reflex/config.py +13 -7
- reflex/custom_components/custom_components.py +23 -25
- reflex/event.py +7 -2
- reflex/istate/data.py +59 -7
- reflex/reflex.py +75 -32
- reflex/state.py +3 -3
- reflex/style.py +3 -3
- reflex/testing.py +4 -10
- reflex/utils/exceptions.py +31 -1
- reflex/utils/exec.py +4 -8
- reflex/utils/export.py +2 -2
- reflex/utils/prerequisites.py +3 -45
- reflex/utils/pyi_generator.py +2 -2
- reflex/utils/redir.py +3 -12
- reflex/vars/base.py +26 -3
- reflex/vars/number.py +22 -21
- reflex/vars/object.py +29 -0
- reflex/vars/sequence.py +37 -0
- {reflex-0.7.2a2.dist-info → reflex-0.7.3a1.dist-info}/METADATA +52 -66
- {reflex-0.7.2a2.dist-info → reflex-0.7.3a1.dist-info}/RECORD +53 -44
- {reflex-0.7.2a2.dist-info → reflex-0.7.3a1.dist-info}/WHEEL +1 -1
- reflex-0.7.3a1.dist-info/entry_points.txt +5 -0
- reflex-0.7.2a2.dist-info/entry_points.txt +0 -3
- {reflex-0.7.2a2.dist-info → reflex-0.7.3a1.dist-info/licenses}/LICENSE +0 -0
|
@@ -66,6 +66,17 @@ _MAPPING = {
|
|
|
66
66
|
"portal",
|
|
67
67
|
"source",
|
|
68
68
|
"svg",
|
|
69
|
+
"text",
|
|
70
|
+
"line",
|
|
71
|
+
"circle",
|
|
72
|
+
"ellipse",
|
|
73
|
+
"rect",
|
|
74
|
+
"polygon",
|
|
75
|
+
"path",
|
|
76
|
+
"stop",
|
|
77
|
+
"linear_gradient",
|
|
78
|
+
"radial_gradient",
|
|
79
|
+
"defs",
|
|
69
80
|
],
|
|
70
81
|
"metadata": [
|
|
71
82
|
"base",
|
|
@@ -128,7 +139,15 @@ _MAPPING = {
|
|
|
128
139
|
|
|
129
140
|
EXCLUDE = ["del_", "Del", "image"]
|
|
130
141
|
for v in _MAPPING.values():
|
|
131
|
-
|
|
142
|
+
from reflex.utils.format import to_camel_case
|
|
143
|
+
|
|
144
|
+
v.extend(
|
|
145
|
+
[
|
|
146
|
+
to_camel_case(mod)[0].upper() + to_camel_case(mod)[1:]
|
|
147
|
+
for mod in v
|
|
148
|
+
if mod not in EXCLUDE
|
|
149
|
+
]
|
|
150
|
+
)
|
|
132
151
|
|
|
133
152
|
_SUBMOD_ATTRS: dict[str, list[str]] = _MAPPING
|
|
134
153
|
|
|
@@ -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
|
-
|
|
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
|
reflex/components/lucide/icon.py
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
82
|
-
f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(icons_sorted[0:
|
|
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]
|
|
@@ -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
|
|
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
|
-
|
|
962
|
-
|
|
963
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
11
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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)
|