reflex 0.7.12a1__py3-none-any.whl → 0.7.13__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/app/rxconfig.py.jinja2 +1 -0
- reflex/.templates/web/postcss.config.js +0 -1
- reflex/.templates/web/utils/state.js +1 -1
- reflex/__init__.py +1 -0
- reflex/__init__.pyi +1 -0
- reflex/app.py +101 -29
- reflex/compiler/compiler.py +11 -62
- reflex/compiler/templates.py +12 -3
- reflex/compiler/utils.py +20 -4
- reflex/components/component.py +366 -88
- reflex/components/core/helmet.pyi +66 -0
- reflex/components/datadisplay/code.py +1 -1
- reflex/components/datadisplay/shiki_code_block.py +97 -86
- reflex/components/datadisplay/shiki_code_block.pyi +4 -2
- reflex/components/el/elements/forms.py +1 -1
- reflex/components/lucide/icon.py +2 -1
- reflex/components/lucide/icon.pyi +1 -0
- reflex/components/plotly/plotly.py +2 -2
- reflex/components/plotly/plotly.pyi +2 -3
- reflex/components/radix/primitives/accordion.py +1 -1
- reflex/components/radix/primitives/drawer.py +1 -1
- reflex/components/radix/primitives/form.py +1 -1
- reflex/components/radix/themes/base.py +4 -11
- reflex/components/radix/themes/components/icon_button.py +2 -2
- reflex/components/radix/themes/components/text_field.py +3 -0
- reflex/components/radix/themes/components/text_field.pyi +2 -0
- reflex/components/radix/themes/layout/list.py +1 -1
- reflex/components/tags/iter_tag.py +3 -5
- reflex/config.py +57 -7
- reflex/constants/__init__.py +0 -2
- reflex/event.py +154 -93
- reflex/experimental/client_state.py +3 -1
- reflex/plugins/__init__.py +7 -0
- reflex/plugins/base.py +101 -0
- reflex/plugins/tailwind_v3.py +255 -0
- reflex/plugins/tailwind_v4.py +258 -0
- reflex/state.py +24 -3
- reflex/utils/build.py +1 -1
- reflex/utils/console.py +1 -1
- reflex/utils/exec.py +23 -0
- reflex/utils/path_ops.py +26 -6
- reflex/utils/prerequisites.py +21 -90
- reflex/utils/pyi_generator.py +12 -2
- reflex/utils/types.py +15 -1
- reflex/vars/base.py +59 -4
- reflex/vars/object.py +8 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/METADATA +2 -2
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/RECORD +52 -50
- scripts/hatch_build.py +17 -0
- reflex/.templates/jinja/web/tailwind.config.js.jinja2 +0 -66
- reflex/.templates/web/styles/tailwind.css +0 -6
- reflex/constants/style.py +0 -16
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/WHEEL +0 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/entry_points.txt +0 -0
- {reflex-0.7.12a1.dist-info → reflex-0.7.13.dist-info}/licenses/LICENSE +0 -0
reflex/config.py
CHANGED
|
@@ -22,6 +22,7 @@ from typing import (
|
|
|
22
22
|
TYPE_CHECKING,
|
|
23
23
|
Annotated,
|
|
24
24
|
Any,
|
|
25
|
+
ClassVar,
|
|
25
26
|
Generic,
|
|
26
27
|
TypeVar,
|
|
27
28
|
get_args,
|
|
@@ -34,6 +35,7 @@ import pydantic.v1 as pydantic
|
|
|
34
35
|
from reflex import constants
|
|
35
36
|
from reflex.base import Base
|
|
36
37
|
from reflex.constants.base import LogLevel
|
|
38
|
+
from reflex.plugins import Plugin, TailwindV3Plugin, TailwindV4Plugin
|
|
37
39
|
from reflex.utils import console
|
|
38
40
|
from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
|
|
39
41
|
from reflex.utils.types import (
|
|
@@ -748,6 +750,9 @@ class EnvironmentVariables:
|
|
|
748
750
|
# The timeout to wait for a pong from the websocket server in seconds.
|
|
749
751
|
REFLEX_SOCKET_TIMEOUT: EnvVar[int] = env_var(constants.Ping.TIMEOUT)
|
|
750
752
|
|
|
753
|
+
# Whether to run Granian in a spawn process. This enables Reflex to pick up on environment variable changes between hot reloads.
|
|
754
|
+
REFLEX_STRICT_HOT_RELOAD: EnvVar[bool] = env_var(False)
|
|
755
|
+
|
|
751
756
|
|
|
752
757
|
environment = EnvironmentVariables()
|
|
753
758
|
|
|
@@ -891,6 +896,11 @@ class Config(Base):
|
|
|
891
896
|
# Extra overlay function to run after the app is built. Formatted such that `from path_0.path_1... import path[-1]`, and calling it with no arguments would work. For example, "reflex.components.moment.moment".
|
|
892
897
|
extra_overlay_function: str | None = None
|
|
893
898
|
|
|
899
|
+
# List of plugins to use in the app.
|
|
900
|
+
plugins: list[Plugin] = []
|
|
901
|
+
|
|
902
|
+
_prefixes: ClassVar[list[str]] = ["REFLEX_"]
|
|
903
|
+
|
|
894
904
|
def __init__(self, *args, **kwargs):
|
|
895
905
|
"""Initialize the config values.
|
|
896
906
|
|
|
@@ -903,29 +913,59 @@ class Config(Base):
|
|
|
903
913
|
"""
|
|
904
914
|
super().__init__(*args, **kwargs)
|
|
905
915
|
|
|
906
|
-
#
|
|
907
|
-
|
|
916
|
+
# Clean up this code when we remove plain envvar in 0.8.0
|
|
917
|
+
show_deprecation = False
|
|
918
|
+
env_loglevel = os.environ.get("REFLEX_LOGLEVEL")
|
|
919
|
+
if not env_loglevel:
|
|
920
|
+
env_loglevel = os.environ.get("LOGLEVEL")
|
|
921
|
+
if env_loglevel:
|
|
922
|
+
show_deprecation = True
|
|
908
923
|
if env_loglevel is not None:
|
|
909
924
|
env_loglevel = LogLevel(env_loglevel.lower())
|
|
910
925
|
if env_loglevel or self.loglevel != LogLevel.DEFAULT:
|
|
911
926
|
console.set_log_level(env_loglevel or self.loglevel)
|
|
912
927
|
|
|
928
|
+
if show_deprecation:
|
|
929
|
+
console.deprecate(
|
|
930
|
+
"Usage of deprecated LOGLEVEL env var detected.",
|
|
931
|
+
reason="Prefer `REFLEX_` prefix when setting env vars.",
|
|
932
|
+
deprecation_version="0.7.13",
|
|
933
|
+
removal_version="0.8.0",
|
|
934
|
+
)
|
|
935
|
+
|
|
913
936
|
# Update the config from environment variables.
|
|
914
937
|
env_kwargs = self.update_from_env()
|
|
915
938
|
for key, env_value in env_kwargs.items():
|
|
916
939
|
setattr(self, key, env_value)
|
|
917
940
|
|
|
918
|
-
#
|
|
941
|
+
# Update default URLs if ports were set
|
|
919
942
|
kwargs.update(env_kwargs)
|
|
920
943
|
self._non_default_attributes.update(kwargs)
|
|
921
944
|
self._replace_defaults(**kwargs)
|
|
922
945
|
|
|
946
|
+
if self.tailwind is not None and not any(
|
|
947
|
+
isinstance(plugin, (TailwindV3Plugin, TailwindV4Plugin))
|
|
948
|
+
for plugin in self.plugins
|
|
949
|
+
):
|
|
950
|
+
console.deprecate(
|
|
951
|
+
"Inferring tailwind usage",
|
|
952
|
+
reason="""
|
|
953
|
+
|
|
954
|
+
If you are using tailwind, add `rx.plugins.TailwindV3Plugin()` to the `plugins=[]` in rxconfig.py.
|
|
955
|
+
|
|
956
|
+
If you are not using tailwind, set `tailwind` to `None` in rxconfig.py.""",
|
|
957
|
+
deprecation_version="0.7.13",
|
|
958
|
+
removal_version="0.8.0",
|
|
959
|
+
dedupe=True,
|
|
960
|
+
)
|
|
961
|
+
self.plugins.append(TailwindV3Plugin())
|
|
962
|
+
|
|
923
963
|
if (
|
|
924
964
|
self.state_manager_mode == constants.StateManagerMode.REDIS
|
|
925
965
|
and not self.redis_url
|
|
926
966
|
):
|
|
927
967
|
raise ConfigError(
|
|
928
|
-
"REDIS_URL is required when using the redis state manager."
|
|
968
|
+
f"{self._prefixes[0]}REDIS_URL is required when using the redis state manager."
|
|
929
969
|
)
|
|
930
970
|
|
|
931
971
|
@property
|
|
@@ -966,7 +1006,18 @@ class Config(Base):
|
|
|
966
1006
|
# Iterate over the fields.
|
|
967
1007
|
for key, field in self.__fields__.items():
|
|
968
1008
|
# The env var name is the key in uppercase.
|
|
969
|
-
|
|
1009
|
+
for prefix in self._prefixes:
|
|
1010
|
+
if env_var := os.environ.get(f"{prefix}{key.upper()}"):
|
|
1011
|
+
break
|
|
1012
|
+
else:
|
|
1013
|
+
# Default to non-prefixed env var if other are not found.
|
|
1014
|
+
if env_var := os.environ.get(key.upper()):
|
|
1015
|
+
console.deprecate(
|
|
1016
|
+
f"Usage of deprecated {key.upper()} env var detected.",
|
|
1017
|
+
reason=f"Prefer `{self._prefixes[0]}` prefix when setting env vars.",
|
|
1018
|
+
deprecation_version="0.7.13",
|
|
1019
|
+
removal_version="0.8.0",
|
|
1020
|
+
)
|
|
970
1021
|
|
|
971
1022
|
# If the env var is set, override the config value.
|
|
972
1023
|
if env_var and env_var.strip():
|
|
@@ -986,7 +1037,6 @@ class Config(Base):
|
|
|
986
1037
|
f"Overriding config value {key} with env var {key.upper()}={env_var}",
|
|
987
1038
|
dedupe=True,
|
|
988
1039
|
)
|
|
989
|
-
|
|
990
1040
|
return updated_values
|
|
991
1041
|
|
|
992
1042
|
def get_event_namespace(self) -> str:
|
|
@@ -1038,7 +1088,7 @@ class Config(Base):
|
|
|
1038
1088
|
"""
|
|
1039
1089
|
for key, value in kwargs.items():
|
|
1040
1090
|
if value is not None:
|
|
1041
|
-
os.environ[key.upper()] = str(value)
|
|
1091
|
+
os.environ[self._prefixes[0] + key.upper()] = str(value)
|
|
1042
1092
|
setattr(self, key, value)
|
|
1043
1093
|
self._non_default_attributes.update(kwargs)
|
|
1044
1094
|
self._replace_defaults(**kwargs)
|
reflex/constants/__init__.py
CHANGED
|
@@ -59,7 +59,6 @@ from .route import (
|
|
|
59
59
|
RouteVar,
|
|
60
60
|
)
|
|
61
61
|
from .state import StateManagerMode
|
|
62
|
-
from .style import Tailwind
|
|
63
62
|
|
|
64
63
|
__all__ = [
|
|
65
64
|
"ALEMBIC_CONFIG",
|
|
@@ -115,6 +114,5 @@ __all__ = [
|
|
|
115
114
|
"RouteVar",
|
|
116
115
|
"SocketEvent",
|
|
117
116
|
"StateManagerMode",
|
|
118
|
-
"Tailwind",
|
|
119
117
|
"Templates",
|
|
120
118
|
]
|
reflex/event.py
CHANGED
|
@@ -1262,6 +1262,118 @@ def get_hydrate_event(state: BaseState) -> str:
|
|
|
1262
1262
|
return get_event(state, constants.CompileVars.HYDRATE)
|
|
1263
1263
|
|
|
1264
1264
|
|
|
1265
|
+
def _values_returned_from_event(
|
|
1266
|
+
event_spec: ArgsSpec | Sequence[ArgsSpec],
|
|
1267
|
+
) -> list[Any]:
|
|
1268
|
+
return [
|
|
1269
|
+
event_spec_return_type
|
|
1270
|
+
for arg_spec in (
|
|
1271
|
+
[event_spec] if not isinstance(event_spec, Sequence) else list(event_spec)
|
|
1272
|
+
)
|
|
1273
|
+
if (event_spec_return_type := get_type_hints(arg_spec).get("return", None))
|
|
1274
|
+
is not None
|
|
1275
|
+
and get_origin(event_spec_return_type) is tuple
|
|
1276
|
+
]
|
|
1277
|
+
|
|
1278
|
+
|
|
1279
|
+
def _check_event_args_subclass_of_callback(
|
|
1280
|
+
callback_params_names: list[str],
|
|
1281
|
+
provided_event_types: list[Any],
|
|
1282
|
+
callback_param_name_to_type: dict[str, Any],
|
|
1283
|
+
callback_name: str = "",
|
|
1284
|
+
key: str = "",
|
|
1285
|
+
):
|
|
1286
|
+
"""Check if the event handler arguments are subclass of the callback.
|
|
1287
|
+
|
|
1288
|
+
Args:
|
|
1289
|
+
callback_params_names: The names of the callback parameters.
|
|
1290
|
+
provided_event_types: The event types.
|
|
1291
|
+
callback_param_name_to_type: The callback parameter name to type mapping.
|
|
1292
|
+
callback_name: The name of the callback.
|
|
1293
|
+
key: The key.
|
|
1294
|
+
|
|
1295
|
+
Raises:
|
|
1296
|
+
TypeError: If the event handler arguments are invalid.
|
|
1297
|
+
EventHandlerArgTypeMismatchError: If the event handler arguments do not match the callback.
|
|
1298
|
+
|
|
1299
|
+
# noqa: DAR401 delayed_exceptions[]
|
|
1300
|
+
# noqa: DAR402 EventHandlerArgTypeMismatchError
|
|
1301
|
+
"""
|
|
1302
|
+
type_match_found: dict[str, bool] = {}
|
|
1303
|
+
delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
|
|
1304
|
+
|
|
1305
|
+
for event_spec_index, event_spec_return_type in enumerate(provided_event_types):
|
|
1306
|
+
args = get_args(event_spec_return_type)
|
|
1307
|
+
|
|
1308
|
+
args_types_without_vars = [
|
|
1309
|
+
arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
|
|
1310
|
+
]
|
|
1311
|
+
|
|
1312
|
+
# check that args of event handler are matching the spec if type hints are provided
|
|
1313
|
+
for i, arg in enumerate(callback_params_names[: len(args_types_without_vars)]):
|
|
1314
|
+
if arg not in callback_param_name_to_type:
|
|
1315
|
+
continue
|
|
1316
|
+
|
|
1317
|
+
type_match_found.setdefault(arg, False)
|
|
1318
|
+
|
|
1319
|
+
try:
|
|
1320
|
+
compare_result = typehint_issubclass(
|
|
1321
|
+
args_types_without_vars[i], callback_param_name_to_type[arg]
|
|
1322
|
+
)
|
|
1323
|
+
except TypeError as te:
|
|
1324
|
+
callback_name_context = f" of {callback_name}" if callback_name else ""
|
|
1325
|
+
key_context = f" for {key}" if key else ""
|
|
1326
|
+
raise TypeError(
|
|
1327
|
+
f"Could not compare types {args_types_without_vars[i]} and {callback_param_name_to_type[arg]} for argument {arg}{callback_name_context}{key_context}."
|
|
1328
|
+
) from te
|
|
1329
|
+
|
|
1330
|
+
if compare_result:
|
|
1331
|
+
type_match_found[arg] = True
|
|
1332
|
+
continue
|
|
1333
|
+
else:
|
|
1334
|
+
type_match_found[arg] = False
|
|
1335
|
+
as_annotated_in = (
|
|
1336
|
+
f" as annotated in {callback_name}" if callback_name else ""
|
|
1337
|
+
)
|
|
1338
|
+
delayed_exceptions.append(
|
|
1339
|
+
EventHandlerArgTypeMismatchError(
|
|
1340
|
+
f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {callback_param_name_to_type[arg]}{as_annotated_in} instead."
|
|
1341
|
+
)
|
|
1342
|
+
)
|
|
1343
|
+
|
|
1344
|
+
if all(type_match_found.values()):
|
|
1345
|
+
delayed_exceptions.clear()
|
|
1346
|
+
if event_spec_index:
|
|
1347
|
+
args = get_args(provided_event_types[0])
|
|
1348
|
+
|
|
1349
|
+
args_types_without_vars = [
|
|
1350
|
+
arg if get_origin(arg) is not Var else get_args(arg)[0]
|
|
1351
|
+
for arg in args
|
|
1352
|
+
]
|
|
1353
|
+
|
|
1354
|
+
expect_string = ", ".join(
|
|
1355
|
+
repr(arg) for arg in args_types_without_vars
|
|
1356
|
+
).replace("[", "\\[")
|
|
1357
|
+
|
|
1358
|
+
given_string = ", ".join(
|
|
1359
|
+
repr(callback_param_name_to_type.get(arg, Any))
|
|
1360
|
+
for arg in callback_params_names
|
|
1361
|
+
).replace("[", "\\[")
|
|
1362
|
+
|
|
1363
|
+
as_annotated_in = (
|
|
1364
|
+
f" as annotated in {callback_name}" if callback_name else ""
|
|
1365
|
+
)
|
|
1366
|
+
|
|
1367
|
+
console.warn(
|
|
1368
|
+
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> (){as_annotated_in} instead. "
|
|
1369
|
+
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
|
|
1370
|
+
)
|
|
1371
|
+
break
|
|
1372
|
+
|
|
1373
|
+
if delayed_exceptions:
|
|
1374
|
+
raise delayed_exceptions[0]
|
|
1375
|
+
|
|
1376
|
+
|
|
1265
1377
|
def call_event_handler(
|
|
1266
1378
|
event_callback: EventHandler | EventSpec,
|
|
1267
1379
|
event_spec: ArgsSpec | Sequence[ArgsSpec],
|
|
@@ -1278,17 +1390,13 @@ def call_event_handler(
|
|
|
1278
1390
|
event_spec: The lambda that define the argument(s) to pass to the event handler.
|
|
1279
1391
|
key: The key to pass to the event handler.
|
|
1280
1392
|
|
|
1281
|
-
Raises:
|
|
1282
|
-
EventHandlerArgTypeMismatchError: If the event handler arguments do not match the event spec. #noqa: DAR402
|
|
1283
|
-
TypeError: If the event handler arguments are invalid.
|
|
1284
|
-
|
|
1285
1393
|
Returns:
|
|
1286
1394
|
The event spec from calling the event handler.
|
|
1287
|
-
|
|
1288
|
-
#noqa: DAR401
|
|
1289
1395
|
"""
|
|
1290
1396
|
event_spec_args = parse_args_spec(event_spec)
|
|
1291
1397
|
|
|
1398
|
+
event_spec_return_types = _values_returned_from_event(event_spec)
|
|
1399
|
+
|
|
1292
1400
|
if isinstance(event_callback, EventSpec):
|
|
1293
1401
|
check_fn_match_arg_spec(
|
|
1294
1402
|
event_callback.handler.fn,
|
|
@@ -1297,6 +1405,32 @@ def call_event_handler(
|
|
|
1297
1405
|
bool(event_callback.handler.state_full_name) + len(event_callback.args),
|
|
1298
1406
|
event_callback.handler.fn.__qualname__,
|
|
1299
1407
|
)
|
|
1408
|
+
|
|
1409
|
+
event_callback_spec_args = list(
|
|
1410
|
+
inspect.signature(event_callback.handler.fn).parameters.keys()
|
|
1411
|
+
)
|
|
1412
|
+
|
|
1413
|
+
try:
|
|
1414
|
+
type_hints_of_provided_callback = get_type_hints(event_callback.handler.fn)
|
|
1415
|
+
except NameError:
|
|
1416
|
+
type_hints_of_provided_callback = {}
|
|
1417
|
+
|
|
1418
|
+
argument_names = [str(arg) for arg, value in event_callback.args]
|
|
1419
|
+
|
|
1420
|
+
_check_event_args_subclass_of_callback(
|
|
1421
|
+
[
|
|
1422
|
+
arg
|
|
1423
|
+
for arg in event_callback_spec_args[
|
|
1424
|
+
bool(event_callback.handler.state_full_name) :
|
|
1425
|
+
]
|
|
1426
|
+
if arg not in argument_names
|
|
1427
|
+
],
|
|
1428
|
+
event_spec_return_types,
|
|
1429
|
+
type_hints_of_provided_callback,
|
|
1430
|
+
event_callback.handler.fn.__qualname__,
|
|
1431
|
+
key or "",
|
|
1432
|
+
)
|
|
1433
|
+
|
|
1300
1434
|
# Handle partial application of EventSpec args
|
|
1301
1435
|
return event_callback.add_args(*event_spec_args)
|
|
1302
1436
|
|
|
@@ -1308,98 +1442,23 @@ def call_event_handler(
|
|
|
1308
1442
|
event_callback.fn.__qualname__,
|
|
1309
1443
|
)
|
|
1310
1444
|
|
|
1311
|
-
all_acceptable_specs = (
|
|
1312
|
-
[event_spec] if not isinstance(event_spec, Sequence) else event_spec
|
|
1313
|
-
)
|
|
1314
|
-
|
|
1315
|
-
event_spec_return_types = list(
|
|
1316
|
-
filter(
|
|
1317
|
-
lambda event_spec_return_type: event_spec_return_type is not None
|
|
1318
|
-
and get_origin(event_spec_return_type) is tuple,
|
|
1319
|
-
(
|
|
1320
|
-
get_type_hints(arg_spec).get("return", None)
|
|
1321
|
-
for arg_spec in all_acceptable_specs
|
|
1322
|
-
),
|
|
1323
|
-
)
|
|
1324
|
-
)
|
|
1325
|
-
type_match_found: dict[str, bool] = {}
|
|
1326
|
-
delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
|
|
1327
|
-
|
|
1328
|
-
try:
|
|
1329
|
-
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
|
1330
|
-
except NameError:
|
|
1331
|
-
type_hints_of_provided_callback = {}
|
|
1332
|
-
|
|
1333
1445
|
if event_spec_return_types:
|
|
1334
1446
|
event_callback_spec_args = list(
|
|
1335
1447
|
inspect.signature(event_callback.fn).parameters.keys()
|
|
1336
1448
|
)
|
|
1337
1449
|
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
):
|
|
1351
|
-
if arg not in type_hints_of_provided_callback:
|
|
1352
|
-
continue
|
|
1353
|
-
|
|
1354
|
-
type_match_found.setdefault(arg, False)
|
|
1355
|
-
|
|
1356
|
-
try:
|
|
1357
|
-
compare_result = typehint_issubclass(
|
|
1358
|
-
args_types_without_vars[i], type_hints_of_provided_callback[arg]
|
|
1359
|
-
)
|
|
1360
|
-
except TypeError as te:
|
|
1361
|
-
raise TypeError(
|
|
1362
|
-
f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
|
|
1363
|
-
) from te
|
|
1364
|
-
|
|
1365
|
-
if compare_result:
|
|
1366
|
-
type_match_found[arg] = True
|
|
1367
|
-
continue
|
|
1368
|
-
else:
|
|
1369
|
-
type_match_found[arg] = False
|
|
1370
|
-
delayed_exceptions.append(
|
|
1371
|
-
EventHandlerArgTypeMismatchError(
|
|
1372
|
-
f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} as annotated in {event_callback.fn.__qualname__} instead."
|
|
1373
|
-
)
|
|
1374
|
-
)
|
|
1375
|
-
|
|
1376
|
-
if all(type_match_found.values()):
|
|
1377
|
-
delayed_exceptions.clear()
|
|
1378
|
-
if event_spec_index:
|
|
1379
|
-
args = get_args(event_spec_return_types[0])
|
|
1380
|
-
|
|
1381
|
-
args_types_without_vars = [
|
|
1382
|
-
arg if get_origin(arg) is not Var else get_args(arg)[0]
|
|
1383
|
-
for arg in args
|
|
1384
|
-
]
|
|
1385
|
-
|
|
1386
|
-
expect_string = ", ".join(
|
|
1387
|
-
repr(arg) for arg in args_types_without_vars
|
|
1388
|
-
).replace("[", "\\[")
|
|
1389
|
-
|
|
1390
|
-
given_string = ", ".join(
|
|
1391
|
-
repr(type_hints_of_provided_callback.get(arg, Any))
|
|
1392
|
-
for arg in event_callback_spec_args[1:]
|
|
1393
|
-
).replace("[", "\\[")
|
|
1394
|
-
|
|
1395
|
-
console.warn(
|
|
1396
|
-
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
|
|
1397
|
-
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
|
|
1398
|
-
)
|
|
1399
|
-
break
|
|
1400
|
-
|
|
1401
|
-
if delayed_exceptions:
|
|
1402
|
-
raise delayed_exceptions[0]
|
|
1450
|
+
try:
|
|
1451
|
+
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
|
1452
|
+
except NameError:
|
|
1453
|
+
type_hints_of_provided_callback = {}
|
|
1454
|
+
|
|
1455
|
+
_check_event_args_subclass_of_callback(
|
|
1456
|
+
event_callback_spec_args[1:],
|
|
1457
|
+
event_spec_return_types,
|
|
1458
|
+
type_hints_of_provided_callback,
|
|
1459
|
+
event_callback.fn.__qualname__,
|
|
1460
|
+
key or "",
|
|
1461
|
+
)
|
|
1403
1462
|
|
|
1404
1463
|
return event_callback(*event_spec_args)
|
|
1405
1464
|
|
|
@@ -1958,6 +2017,8 @@ class EventCallback(Generic[Unpack[P]], EventActionsMixin):
|
|
|
1958
2017
|
class LambdaEventCallback(Protocol[Unpack[P]]):
|
|
1959
2018
|
"""A protocol for a lambda event callback."""
|
|
1960
2019
|
|
|
2020
|
+
__code__: types.CodeType
|
|
2021
|
+
|
|
1961
2022
|
@overload
|
|
1962
2023
|
def __call__(self: LambdaEventCallback[()]) -> Any: ...
|
|
1963
2024
|
|
|
@@ -158,7 +158,9 @@ class ClientStateVar(Var):
|
|
|
158
158
|
hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
|
|
159
159
|
hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
|
|
160
160
|
hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
|
|
161
|
-
hooks[
|
|
161
|
+
hooks[
|
|
162
|
+
f"{_client_state_ref_dict(var_name)}[{id_name}] = {_client_state_ref(var_name)}"
|
|
163
|
+
] = None
|
|
162
164
|
hooks[
|
|
163
165
|
f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
|
|
164
166
|
] = None
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Reflex Plugin System."""
|
|
2
|
+
|
|
3
|
+
from .base import CommonContext as CommonContext
|
|
4
|
+
from .base import Plugin as Plugin
|
|
5
|
+
from .base import PreCompileContext as PreCompileContext
|
|
6
|
+
from .tailwind_v3 import Plugin as TailwindV3Plugin
|
|
7
|
+
from .tailwind_v4 import Plugin as TailwindV4Plugin
|
reflex/plugins/base.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Base class for all plugins."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Sequence
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import ParamSpec, Protocol, TypedDict
|
|
6
|
+
|
|
7
|
+
from typing_extensions import Unpack
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CommonContext(TypedDict):
|
|
11
|
+
"""Common context for all plugins."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
P = ParamSpec("P")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AddTaskProtcol(Protocol):
|
|
18
|
+
"""Protocol for adding a task to the pre-compile context."""
|
|
19
|
+
|
|
20
|
+
def __call__(
|
|
21
|
+
self,
|
|
22
|
+
task: Callable[P, list[tuple[str, str]] | tuple[str, str] | None],
|
|
23
|
+
/,
|
|
24
|
+
*args: P.args,
|
|
25
|
+
**kwargs: P.kwargs,
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Add a task to the pre-compile context.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
task: The task to add.
|
|
31
|
+
args: The arguments to pass to the task
|
|
32
|
+
kwargs: The keyword arguments to pass to the task
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class PreCompileContext(CommonContext):
|
|
37
|
+
"""Context for pre-compile hooks."""
|
|
38
|
+
|
|
39
|
+
add_save_task: AddTaskProtcol
|
|
40
|
+
add_modify_task: Callable[[str, Callable[[str], str]], None]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Plugin:
|
|
44
|
+
"""Base class for all plugins."""
|
|
45
|
+
|
|
46
|
+
def get_frontend_development_dependencies(
|
|
47
|
+
self, **context: Unpack[CommonContext]
|
|
48
|
+
) -> list[str] | set[str] | tuple[str, ...]:
|
|
49
|
+
"""Get the NPM packages required by the plugin for development.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
context: The context for the plugin.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
A list of packages required by the plugin for development.
|
|
56
|
+
"""
|
|
57
|
+
return []
|
|
58
|
+
|
|
59
|
+
def get_frontend_dependencies(
|
|
60
|
+
self, **context: Unpack[CommonContext]
|
|
61
|
+
) -> list[str] | set[str] | tuple[str, ...]:
|
|
62
|
+
"""Get the NPM packages required by the plugin.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
context: The context for the plugin.
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
A list of packages required by the plugin.
|
|
69
|
+
"""
|
|
70
|
+
return []
|
|
71
|
+
|
|
72
|
+
def get_static_assets(
|
|
73
|
+
self, **context: Unpack[CommonContext]
|
|
74
|
+
) -> Sequence[tuple[Path, str | bytes]]:
|
|
75
|
+
"""Get the static assets required by the plugin.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
context: The context for the plugin.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
A list of static assets required by the plugin.
|
|
82
|
+
"""
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
def get_stylesheet_paths(self, **context: Unpack[CommonContext]) -> Sequence[str]:
|
|
86
|
+
"""Get the paths to the stylesheets required by the plugin relative to the styles directory.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
context: The context for the plugin.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
A list of paths to the stylesheets required by the plugin.
|
|
93
|
+
"""
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
def pre_compile(self, **context: Unpack[PreCompileContext]) -> None:
|
|
97
|
+
"""Called before the compilation of the plugin.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
context: The context for the plugin.
|
|
101
|
+
"""
|