reflex 0.6.3a4__py3-none-any.whl → 0.6.4__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.
- reflex/.templates/jinja/web/pages/_app.js.jinja2 +2 -2
- reflex/.templates/jinja/web/utils/context.js.jinja2 +3 -1
- reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js +3 -3
- reflex/.templates/web/components/shiki/code.js +29 -0
- reflex/.templates/web/jsconfig.json +2 -1
- reflex/.templates/web/utils/state.js +6 -4
- reflex/__init__.py +6 -3
- reflex/__init__.pyi +4 -3
- reflex/app.py +6 -5
- reflex/compiler/compiler.py +6 -7
- reflex/compiler/utils.py +8 -1
- reflex/components/base/error_boundary.py +37 -24
- reflex/components/base/error_boundary.pyi +8 -7
- reflex/components/component.py +9 -4
- reflex/components/core/banner.py +2 -2
- reflex/components/core/client_side_routing.py +1 -1
- reflex/components/core/clipboard.py +1 -1
- reflex/components/core/clipboard.pyi +1 -1
- reflex/components/core/cond.py +1 -1
- reflex/components/core/debounce.py +5 -1
- reflex/components/core/upload.py +7 -9
- reflex/components/core/upload.pyi +2 -2
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/dataeditor.py +83 -18
- reflex/components/datadisplay/dataeditor.pyi +67 -15
- reflex/components/datadisplay/shiki_code_block.py +813 -0
- reflex/components/datadisplay/shiki_code_block.pyi +2211 -0
- reflex/components/dynamic.py +3 -3
- reflex/components/el/elements/forms.py +37 -23
- reflex/components/el/elements/forms.pyi +7 -4
- reflex/components/markdown/markdown.py +12 -1
- reflex/components/moment/moment.pyi +1 -1
- reflex/components/radix/primitives/drawer.pyi +2 -2
- reflex/components/radix/themes/base.py +2 -2
- reflex/components/radix/themes/color_mode.pyi +1 -1
- reflex/components/radix/themes/components/alert_dialog.pyi +1 -1
- reflex/components/radix/themes/components/checkbox.pyi +3 -3
- reflex/components/radix/themes/components/context_menu.pyi +1 -1
- reflex/components/radix/themes/components/dialog.pyi +2 -2
- reflex/components/radix/themes/components/dropdown_menu.pyi +2 -2
- reflex/components/radix/themes/components/hover_card.pyi +2 -2
- reflex/components/radix/themes/components/popover.pyi +1 -1
- reflex/components/radix/themes/components/radio_cards.pyi +1 -1
- reflex/components/radix/themes/components/radio_group.pyi +1 -1
- reflex/components/radix/themes/components/select.pyi +6 -6
- reflex/components/radix/themes/components/switch.pyi +1 -1
- reflex/components/radix/themes/components/tabs.pyi +2 -2
- reflex/components/radix/themes/components/tooltip.pyi +4 -2
- reflex/components/react_player/__init__.py +1 -0
- reflex/components/react_player/audio.pyi +6 -3
- reflex/components/react_player/react_player.py +12 -1
- reflex/components/react_player/react_player.pyi +11 -3
- reflex/components/react_player/video.pyi +6 -3
- reflex/components/recharts/recharts.py +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/components/suneditor/editor.py +40 -16
- reflex/components/suneditor/editor.pyi +15 -11
- reflex/config.py +284 -20
- reflex/constants/__init__.py +2 -0
- reflex/constants/base.py +53 -31
- reflex/constants/compiler.py +2 -12
- reflex/constants/config.py +1 -2
- reflex/constants/installer.py +88 -32
- reflex/constants/style.py +1 -1
- reflex/constants/utils.py +32 -0
- reflex/custom_components/custom_components.py +3 -3
- reflex/event.py +152 -84
- reflex/experimental/__init__.py +2 -0
- reflex/experimental/client_state.py +1 -1
- reflex/experimental/layout.pyi +1 -1
- reflex/istate/storage.py +144 -0
- reflex/model.py +8 -11
- reflex/reflex.py +18 -17
- reflex/state.py +89 -151
- reflex/style.py +1 -1
- reflex/testing.py +2 -1
- reflex/utils/build.py +0 -12
- reflex/utils/exceptions.py +8 -0
- reflex/utils/exec.py +22 -4
- reflex/utils/imports.py +6 -0
- reflex/utils/net.py +2 -4
- reflex/utils/path_ops.py +7 -21
- reflex/utils/prerequisites.py +11 -17
- reflex/utils/pyi_generator.py +91 -3
- reflex/utils/registry.py +2 -6
- reflex/utils/types.py +14 -0
- reflex/vars/base.py +453 -424
- reflex/vars/function.py +6 -16
- reflex/vars/number.py +46 -67
- reflex/vars/object.py +1 -31
- reflex/vars/sequence.py +177 -47
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/METADATA +1 -1
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/RECORD +96 -91
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/LICENSE +0 -0
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/WHEEL +0 -0
- {reflex-0.6.3a4.dist-info → reflex-0.6.4.dist-info}/entry_points.txt +0 -0
reflex/utils/exec.py
CHANGED
|
@@ -15,7 +15,7 @@ from urllib.parse import urljoin
|
|
|
15
15
|
import psutil
|
|
16
16
|
|
|
17
17
|
from reflex import constants
|
|
18
|
-
from reflex.config import get_config
|
|
18
|
+
from reflex.config import environment, get_config
|
|
19
19
|
from reflex.constants.base import LogLevel
|
|
20
20
|
from reflex.utils import console, path_ops
|
|
21
21
|
from reflex.utils.prerequisites import get_web_dir
|
|
@@ -184,7 +184,7 @@ def should_use_granian():
|
|
|
184
184
|
Returns:
|
|
185
185
|
True if Granian should be used.
|
|
186
186
|
"""
|
|
187
|
-
return
|
|
187
|
+
return environment.REFLEX_USE_GRANIAN
|
|
188
188
|
|
|
189
189
|
|
|
190
190
|
def get_app_module():
|
|
@@ -337,8 +337,8 @@ def run_uvicorn_backend_prod(host, port, loglevel):
|
|
|
337
337
|
|
|
338
338
|
app_module = get_app_module()
|
|
339
339
|
|
|
340
|
-
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split()
|
|
341
|
-
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
|
|
340
|
+
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
|
|
341
|
+
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
|
|
342
342
|
command = (
|
|
343
343
|
[
|
|
344
344
|
*RUN_BACKEND_PROD_WINDOWS,
|
|
@@ -496,6 +496,24 @@ def is_prod_mode() -> bool:
|
|
|
496
496
|
return current_mode == constants.Env.PROD.value
|
|
497
497
|
|
|
498
498
|
|
|
499
|
+
def is_frontend_only() -> bool:
|
|
500
|
+
"""Check if the app is running in frontend-only mode.
|
|
501
|
+
|
|
502
|
+
Returns:
|
|
503
|
+
True if the app is running in frontend-only mode.
|
|
504
|
+
"""
|
|
505
|
+
return os.environ.get(constants.ENV_FRONTEND_ONLY_ENV_VAR, "").lower() == "true"
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def is_backend_only() -> bool:
|
|
509
|
+
"""Check if the app is running in backend-only mode.
|
|
510
|
+
|
|
511
|
+
Returns:
|
|
512
|
+
True if the app is running in backend-only mode.
|
|
513
|
+
"""
|
|
514
|
+
return os.environ.get(constants.ENV_BACKEND_ONLY_ENV_VAR, "").lower() == "true"
|
|
515
|
+
|
|
516
|
+
|
|
499
517
|
def should_skip_compile() -> bool:
|
|
500
518
|
"""Whether the app should skip compile.
|
|
501
519
|
|
reflex/utils/imports.py
CHANGED
|
@@ -23,6 +23,12 @@ def merge_imports(
|
|
|
23
23
|
for lib, fields in (
|
|
24
24
|
import_dict if isinstance(import_dict, tuple) else import_dict.items()
|
|
25
25
|
):
|
|
26
|
+
# If the lib is an absolute path, we need to prefix it with a $
|
|
27
|
+
lib = (
|
|
28
|
+
"$" + lib
|
|
29
|
+
if lib.startswith(("/utils/", "/components/", "/styles/", "/public/"))
|
|
30
|
+
else lib
|
|
31
|
+
)
|
|
26
32
|
if isinstance(fields, (list, tuple, set)):
|
|
27
33
|
all_imports[lib].extend(
|
|
28
34
|
(
|
reflex/utils/net.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Helpers for downloading files from the network."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
import httpx
|
|
6
4
|
|
|
5
|
+
from ..config import environment
|
|
7
6
|
from . import console
|
|
8
7
|
|
|
9
8
|
|
|
@@ -13,8 +12,7 @@ def _httpx_verify_kwarg() -> bool:
|
|
|
13
12
|
Returns:
|
|
14
13
|
True if SSL verification is enabled, False otherwise
|
|
15
14
|
"""
|
|
16
|
-
|
|
17
|
-
return not ssl_no_verify
|
|
15
|
+
return not environment.SSL_NO_VERIFY
|
|
18
16
|
|
|
19
17
|
|
|
20
18
|
def get(url: str, **kwargs) -> httpx.Response:
|
reflex/utils/path_ops.py
CHANGED
|
@@ -9,6 +9,7 @@ import shutil
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
from reflex import constants
|
|
12
|
+
from reflex.config import environment
|
|
12
13
|
|
|
13
14
|
# Shorthand for join.
|
|
14
15
|
join = os.linesep.join
|
|
@@ -129,30 +130,13 @@ def which(program: str | Path) -> str | Path | None:
|
|
|
129
130
|
return shutil.which(str(program))
|
|
130
131
|
|
|
131
132
|
|
|
132
|
-
def use_system_install(var_name: str) -> bool:
|
|
133
|
-
"""Check if the system install should be used.
|
|
134
|
-
|
|
135
|
-
Args:
|
|
136
|
-
var_name: The name of the environment variable.
|
|
137
|
-
|
|
138
|
-
Raises:
|
|
139
|
-
ValueError: If the variable name is invalid.
|
|
140
|
-
|
|
141
|
-
Returns:
|
|
142
|
-
Whether the associated env var should use the system install.
|
|
143
|
-
"""
|
|
144
|
-
if not var_name.startswith("REFLEX_USE_SYSTEM_"):
|
|
145
|
-
raise ValueError("Invalid system install variable name.")
|
|
146
|
-
return os.getenv(var_name, "").lower() in ["true", "1", "yes"]
|
|
147
|
-
|
|
148
|
-
|
|
149
133
|
def use_system_node() -> bool:
|
|
150
134
|
"""Check if the system node should be used.
|
|
151
135
|
|
|
152
136
|
Returns:
|
|
153
137
|
Whether the system node should be used.
|
|
154
138
|
"""
|
|
155
|
-
return
|
|
139
|
+
return environment.REFLEX_USE_SYSTEM_NODE
|
|
156
140
|
|
|
157
141
|
|
|
158
142
|
def use_system_bun() -> bool:
|
|
@@ -161,7 +145,7 @@ def use_system_bun() -> bool:
|
|
|
161
145
|
Returns:
|
|
162
146
|
Whether the system bun should be used.
|
|
163
147
|
"""
|
|
164
|
-
return
|
|
148
|
+
return environment.REFLEX_USE_SYSTEM_BUN
|
|
165
149
|
|
|
166
150
|
|
|
167
151
|
def get_node_bin_path() -> Path | None:
|
|
@@ -185,7 +169,8 @@ def get_node_path() -> str | None:
|
|
|
185
169
|
"""
|
|
186
170
|
node_path = Path(constants.Node.PATH)
|
|
187
171
|
if use_system_node() or not node_path.exists():
|
|
188
|
-
|
|
172
|
+
system_node_path = which("node")
|
|
173
|
+
return str(system_node_path) if system_node_path else None
|
|
189
174
|
return str(node_path)
|
|
190
175
|
|
|
191
176
|
|
|
@@ -197,7 +182,8 @@ def get_npm_path() -> str | None:
|
|
|
197
182
|
"""
|
|
198
183
|
npm_path = Path(constants.Node.NPM_PATH)
|
|
199
184
|
if use_system_node() or not npm_path.exists():
|
|
200
|
-
|
|
185
|
+
system_npm_path = which("npm")
|
|
186
|
+
return str(system_npm_path) if system_npm_path else None
|
|
201
187
|
return str(npm_path)
|
|
202
188
|
|
|
203
189
|
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -33,7 +33,7 @@ from redis.asyncio import Redis
|
|
|
33
33
|
|
|
34
34
|
from reflex import constants, model
|
|
35
35
|
from reflex.compiler import templates
|
|
36
|
-
from reflex.config import Config, get_config
|
|
36
|
+
from reflex.config import Config, environment, get_config
|
|
37
37
|
from reflex.utils import console, net, path_ops, processes
|
|
38
38
|
from reflex.utils.exceptions import GeneratedCodeHasNoFunctionDefs
|
|
39
39
|
from reflex.utils.format import format_library_name
|
|
@@ -69,8 +69,7 @@ def get_web_dir() -> Path:
|
|
|
69
69
|
Returns:
|
|
70
70
|
The working directory.
|
|
71
71
|
"""
|
|
72
|
-
|
|
73
|
-
return workdir
|
|
72
|
+
return environment.REFLEX_WEB_WORKDIR
|
|
74
73
|
|
|
75
74
|
|
|
76
75
|
def _python_version_check():
|
|
@@ -146,14 +145,9 @@ def check_node_version() -> bool:
|
|
|
146
145
|
Whether the version of Node.js is valid.
|
|
147
146
|
"""
|
|
148
147
|
current_version = get_node_version()
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
current_version >= version.parse(constants.Node.MIN_VERSION)
|
|
153
|
-
if constants.IS_WINDOWS or path_ops.use_system_node()
|
|
154
|
-
else current_version == version.parse(constants.Node.VERSION)
|
|
155
|
-
)
|
|
156
|
-
return False
|
|
148
|
+
return current_version is not None and current_version >= version.parse(
|
|
149
|
+
constants.Node.MIN_VERSION
|
|
150
|
+
)
|
|
157
151
|
|
|
158
152
|
|
|
159
153
|
def get_node_version() -> version.Version | None:
|
|
@@ -255,7 +249,7 @@ def windows_npm_escape_hatch() -> bool:
|
|
|
255
249
|
Returns:
|
|
256
250
|
If the user has set REFLEX_USE_NPM.
|
|
257
251
|
"""
|
|
258
|
-
return
|
|
252
|
+
return environment.REFLEX_USE_NPM
|
|
259
253
|
|
|
260
254
|
|
|
261
255
|
def get_app(reload: bool = False) -> ModuleType:
|
|
@@ -997,7 +991,7 @@ def needs_reinit(frontend: bool = True) -> bool:
|
|
|
997
991
|
return False
|
|
998
992
|
|
|
999
993
|
# Make sure the .reflex directory exists.
|
|
1000
|
-
if not
|
|
994
|
+
if not environment.REFLEX_DIR.exists():
|
|
1001
995
|
return True
|
|
1002
996
|
|
|
1003
997
|
# Make sure the .web directory exists in frontend mode.
|
|
@@ -1102,7 +1096,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
|
|
|
1102
1096
|
"""
|
|
1103
1097
|
try:
|
|
1104
1098
|
initialize_reflex_user_directory()
|
|
1105
|
-
installation_id_file =
|
|
1099
|
+
installation_id_file = environment.REFLEX_DIR / "installation_id"
|
|
1106
1100
|
|
|
1107
1101
|
installation_id = None
|
|
1108
1102
|
if installation_id_file.exists():
|
|
@@ -1127,7 +1121,7 @@ def ensure_reflex_installation_id() -> Optional[int]:
|
|
|
1127
1121
|
def initialize_reflex_user_directory():
|
|
1128
1122
|
"""Initialize the reflex user directory."""
|
|
1129
1123
|
# Create the reflex directory.
|
|
1130
|
-
path_ops.mkdir(
|
|
1124
|
+
path_ops.mkdir(environment.REFLEX_DIR)
|
|
1131
1125
|
|
|
1132
1126
|
|
|
1133
1127
|
def initialize_frontend_dependencies():
|
|
@@ -1150,7 +1144,7 @@ def check_db_initialized() -> bool:
|
|
|
1150
1144
|
Returns:
|
|
1151
1145
|
True if alembic is initialized (or if database is not used).
|
|
1152
1146
|
"""
|
|
1153
|
-
if get_config().db_url is not None and not
|
|
1147
|
+
if get_config().db_url is not None and not environment.ALEMBIC_CONFIG.exists():
|
|
1154
1148
|
console.error(
|
|
1155
1149
|
"Database is not initialized. Run [bold]reflex db init[/bold] first."
|
|
1156
1150
|
)
|
|
@@ -1160,7 +1154,7 @@ def check_db_initialized() -> bool:
|
|
|
1160
1154
|
|
|
1161
1155
|
def check_schema_up_to_date():
|
|
1162
1156
|
"""Check if the sqlmodel metadata matches the current database schema."""
|
|
1163
|
-
if get_config().db_url is None or not
|
|
1157
|
+
if get_config().db_url is None or not environment.ALEMBIC_CONFIG.exists():
|
|
1164
1158
|
return
|
|
1165
1159
|
with model.Model.get_db_engine().connect() as connection:
|
|
1166
1160
|
try:
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -16,7 +16,7 @@ from itertools import chain
|
|
|
16
16
|
from multiprocessing import Pool, cpu_count
|
|
17
17
|
from pathlib import Path
|
|
18
18
|
from types import ModuleType, SimpleNamespace
|
|
19
|
-
from typing import Any, Callable, Iterable, Type, get_args
|
|
19
|
+
from typing import Any, Callable, Iterable, Type, get_args, get_origin
|
|
20
20
|
|
|
21
21
|
from reflex.components.component import Component
|
|
22
22
|
from reflex.utils import types as rx_types
|
|
@@ -214,7 +214,9 @@ def _get_type_hint(value, type_hint_globals, is_optional=True) -> str:
|
|
|
214
214
|
return res
|
|
215
215
|
|
|
216
216
|
|
|
217
|
-
def _generate_imports(
|
|
217
|
+
def _generate_imports(
|
|
218
|
+
typing_imports: Iterable[str],
|
|
219
|
+
) -> list[ast.ImportFrom | ast.Import]:
|
|
218
220
|
"""Generate the import statements for the stub file.
|
|
219
221
|
|
|
220
222
|
Args:
|
|
@@ -228,6 +230,7 @@ def _generate_imports(typing_imports: Iterable[str]) -> list[ast.ImportFrom]:
|
|
|
228
230
|
ast.ImportFrom(module=name, names=[ast.alias(name=val) for val in values])
|
|
229
231
|
for name, values in DEFAULT_IMPORTS.items()
|
|
230
232
|
],
|
|
233
|
+
ast.Import([ast.alias("reflex")]),
|
|
231
234
|
]
|
|
232
235
|
|
|
233
236
|
|
|
@@ -372,6 +375,64 @@ def _extract_class_props_as_ast_nodes(
|
|
|
372
375
|
return kwargs
|
|
373
376
|
|
|
374
377
|
|
|
378
|
+
def type_to_ast(typ, cls: type) -> ast.AST:
|
|
379
|
+
"""Converts any type annotation into its AST representation.
|
|
380
|
+
Handles nested generic types, unions, etc.
|
|
381
|
+
|
|
382
|
+
Args:
|
|
383
|
+
typ: The type annotation to convert.
|
|
384
|
+
cls: The class where the type annotation is used.
|
|
385
|
+
|
|
386
|
+
Returns:
|
|
387
|
+
The AST representation of the type annotation.
|
|
388
|
+
"""
|
|
389
|
+
if typ is type(None):
|
|
390
|
+
return ast.Name(id="None")
|
|
391
|
+
|
|
392
|
+
origin = get_origin(typ)
|
|
393
|
+
|
|
394
|
+
# Handle plain types (int, str, custom classes, etc.)
|
|
395
|
+
if origin is None:
|
|
396
|
+
if hasattr(typ, "__name__"):
|
|
397
|
+
if typ.__module__.startswith("reflex."):
|
|
398
|
+
typ_parts = typ.__module__.split(".")
|
|
399
|
+
cls_parts = cls.__module__.split(".")
|
|
400
|
+
|
|
401
|
+
zipped = list(zip(typ_parts, cls_parts, strict=False))
|
|
402
|
+
|
|
403
|
+
if all(a == b for a, b in zipped) and len(typ_parts) == len(cls_parts):
|
|
404
|
+
return ast.Name(id=typ.__name__)
|
|
405
|
+
|
|
406
|
+
return ast.Name(id=typ.__module__ + "." + typ.__name__)
|
|
407
|
+
return ast.Name(id=typ.__name__)
|
|
408
|
+
elif hasattr(typ, "_name"):
|
|
409
|
+
return ast.Name(id=typ._name)
|
|
410
|
+
return ast.Name(id=str(typ))
|
|
411
|
+
|
|
412
|
+
# Get the base type name (List, Dict, Optional, etc.)
|
|
413
|
+
base_name = origin._name if hasattr(origin, "_name") else origin.__name__
|
|
414
|
+
|
|
415
|
+
# Get type arguments
|
|
416
|
+
args = get_args(typ)
|
|
417
|
+
|
|
418
|
+
# Handle empty type arguments
|
|
419
|
+
if not args:
|
|
420
|
+
return ast.Name(id=base_name)
|
|
421
|
+
|
|
422
|
+
# Convert all type arguments recursively
|
|
423
|
+
arg_nodes = [type_to_ast(arg, cls) for arg in args]
|
|
424
|
+
|
|
425
|
+
# Special case for single-argument types (like List[T] or Optional[T])
|
|
426
|
+
if len(arg_nodes) == 1:
|
|
427
|
+
slice_value = arg_nodes[0]
|
|
428
|
+
else:
|
|
429
|
+
slice_value = ast.Tuple(elts=arg_nodes, ctx=ast.Load())
|
|
430
|
+
|
|
431
|
+
return ast.Subscript(
|
|
432
|
+
value=ast.Name(id=base_name), slice=ast.Index(value=slice_value), ctx=ast.Load()
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
|
|
375
436
|
def _get_parent_imports(func):
|
|
376
437
|
_imports = {"reflex.vars": ["Var"]}
|
|
377
438
|
for type_hint in inspect.get_annotations(func).values():
|
|
@@ -430,13 +491,40 @@ def _generate_component_create_functiondef(
|
|
|
430
491
|
def figure_out_return_type(annotation: Any):
|
|
431
492
|
if inspect.isclass(annotation) and issubclass(annotation, inspect._empty):
|
|
432
493
|
return ast.Name(id="Optional[EventType]")
|
|
494
|
+
|
|
495
|
+
if not isinstance(annotation, str) and get_origin(annotation) is tuple:
|
|
496
|
+
arguments = get_args(annotation)
|
|
497
|
+
|
|
498
|
+
arguments_without_var = [
|
|
499
|
+
get_args(argument)[0] if get_origin(argument) == Var else argument
|
|
500
|
+
for argument in arguments
|
|
501
|
+
]
|
|
502
|
+
|
|
503
|
+
# Convert each argument type to its AST representation
|
|
504
|
+
type_args = [type_to_ast(arg, cls=clz) for arg in arguments_without_var]
|
|
505
|
+
|
|
506
|
+
# Join the type arguments with commas for EventType
|
|
507
|
+
args_str = ", ".join(ast.unparse(arg) for arg in type_args)
|
|
508
|
+
|
|
509
|
+
# Create EventType using the joined string
|
|
510
|
+
event_type = ast.Name(id=f"EventType[{args_str}]")
|
|
511
|
+
|
|
512
|
+
# Wrap in Optional
|
|
513
|
+
optional_type = ast.Subscript(
|
|
514
|
+
value=ast.Name(id="Optional"),
|
|
515
|
+
slice=ast.Index(value=event_type),
|
|
516
|
+
ctx=ast.Load(),
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
return ast.Name(id=ast.unparse(optional_type))
|
|
520
|
+
|
|
433
521
|
if isinstance(annotation, str) and annotation.startswith("Tuple["):
|
|
434
522
|
inside_of_tuple = annotation.removeprefix("Tuple[").removesuffix("]")
|
|
435
523
|
|
|
436
524
|
if inside_of_tuple == "()":
|
|
437
525
|
return ast.Name(id="Optional[EventType[[]]]")
|
|
438
526
|
|
|
439
|
-
arguments
|
|
527
|
+
arguments = [""]
|
|
440
528
|
|
|
441
529
|
bracket_count = 0
|
|
442
530
|
|
reflex/utils/registry.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"""Utilities for working with registries."""
|
|
2
2
|
|
|
3
|
-
import os
|
|
4
|
-
|
|
5
3
|
import httpx
|
|
6
4
|
|
|
5
|
+
from reflex.config import environment
|
|
7
6
|
from reflex.utils import console, net
|
|
8
7
|
|
|
9
8
|
|
|
@@ -56,7 +55,4 @@ def _get_npm_registry() -> str:
|
|
|
56
55
|
Returns:
|
|
57
56
|
str:
|
|
58
57
|
"""
|
|
59
|
-
|
|
60
|
-
return npm_registry
|
|
61
|
-
else:
|
|
62
|
-
return get_best_registry()
|
|
58
|
+
return environment.NPM_CONFIG_REGISTRY or get_best_registry()
|
reflex/utils/types.py
CHANGED
|
@@ -274,6 +274,20 @@ def is_optional(cls: GenericType) -> bool:
|
|
|
274
274
|
return is_union(cls) and type(None) in get_args(cls)
|
|
275
275
|
|
|
276
276
|
|
|
277
|
+
def value_inside_optional(cls: GenericType) -> GenericType:
|
|
278
|
+
"""Get the value inside an Optional type or the original type.
|
|
279
|
+
|
|
280
|
+
Args:
|
|
281
|
+
cls: The class to check.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
The value inside the Optional type or the original type.
|
|
285
|
+
"""
|
|
286
|
+
if is_union(cls) and len(args := get_args(cls)) >= 2 and type(None) in args:
|
|
287
|
+
return unionize(*[arg for arg in args if arg is not type(None)])
|
|
288
|
+
return cls
|
|
289
|
+
|
|
290
|
+
|
|
277
291
|
def get_property_hint(attr: Any | None) -> GenericType | None:
|
|
278
292
|
"""Check if an attribute is a property and return its type hint.
|
|
279
293
|
|