reflex 0.6.7a2__py3-none-any.whl → 0.6.8__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 -4
- reflex/.templates/jinja/web/pages/custom_component.js.jinja2 +3 -4
- reflex/.templates/jinja/web/pages/index.js.jinja2 +2 -3
- reflex/.templates/jinja/web/pages/macros.js.jinja2 +38 -0
- reflex/.templates/jinja/web/pages/stateful_component.js.jinja2 +4 -16
- reflex/.templates/jinja/web/utils/context.js.jinja2 +1 -1
- reflex/.templates/web/utils/state.js +9 -4
- reflex/app.py +12 -10
- reflex/compiler/compiler.py +2 -2
- reflex/compiler/templates.py +41 -0
- reflex/compiler/utils.py +1 -1
- reflex/components/base/bare.py +7 -3
- reflex/components/component.py +78 -116
- reflex/components/core/banner.py +1 -1
- reflex/components/core/breakpoints.py +1 -1
- reflex/components/datadisplay/code.py +14 -10
- reflex/components/datadisplay/dataeditor.py +1 -1
- reflex/components/datadisplay/dataeditor.pyi +1 -1
- reflex/components/el/elements/forms.py +7 -5
- reflex/components/el/elements/metadata.py +1 -1
- reflex/components/lucide/icon.py +142 -19
- reflex/components/lucide/icon.pyi +141 -18
- reflex/components/markdown/markdown.py +3 -2
- reflex/components/plotly/plotly.py +3 -3
- reflex/components/plotly/plotly.pyi +3 -3
- reflex/components/radix/primitives/slider.py +1 -1
- reflex/components/radix/themes/layout/center.pyi +1 -1
- reflex/components/radix/themes/layout/flex.py +1 -1
- reflex/components/radix/themes/layout/flex.pyi +1 -1
- reflex/components/radix/themes/layout/grid.py +1 -1
- reflex/components/radix/themes/layout/grid.pyi +1 -1
- reflex/components/radix/themes/layout/spacer.pyi +1 -1
- reflex/components/radix/themes/layout/stack.pyi +3 -3
- reflex/components/radix/themes/typography/link.py +1 -1
- reflex/components/recharts/cartesian.py +2 -2
- reflex/components/recharts/cartesian.pyi +6 -6
- reflex/components/recharts/charts.py +2 -2
- reflex/components/recharts/polar.py +2 -2
- reflex/components/recharts/polar.pyi +2 -2
- reflex/components/sonner/toast.py +1 -1
- reflex/constants/base.py +1 -1
- reflex/constants/compiler.py +1 -0
- reflex/event.py +96 -1
- reflex/experimental/client_state.py +42 -20
- reflex/istate/data.py +3 -3
- reflex/model.py +4 -5
- reflex/page.py +1 -1
- reflex/reflex.py +8 -1
- reflex/state.py +116 -9
- reflex/testing.py +3 -3
- reflex/utils/exceptions.py +4 -0
- reflex/utils/prerequisites.py +37 -20
- reflex/utils/processes.py +2 -2
- reflex/utils/pyi_generator.py +2 -2
- reflex/utils/telemetry.py +6 -4
- reflex/vars/base.py +15 -4
- reflex/vars/sequence.py +37 -0
- {reflex-0.6.7a2.dist-info → reflex-0.6.8.dist-info}/METADATA +2 -2
- {reflex-0.6.7a2.dist-info → reflex-0.6.8.dist-info}/RECORD +62 -61
- {reflex-0.6.7a2.dist-info → reflex-0.6.8.dist-info}/LICENSE +0 -0
- {reflex-0.6.7a2.dist-info → reflex-0.6.8.dist-info}/WHEEL +0 -0
- {reflex-0.6.7a2.dist-info → reflex-0.6.8.dist-info}/entry_points.txt +0 -0
reflex/state.py
CHANGED
|
@@ -107,6 +107,7 @@ from reflex.utils.exceptions import (
|
|
|
107
107
|
StateSchemaMismatchError,
|
|
108
108
|
StateSerializationError,
|
|
109
109
|
StateTooLargeError,
|
|
110
|
+
UnretrievableVarValueError,
|
|
110
111
|
)
|
|
111
112
|
from reflex.utils.exec import is_testing_env
|
|
112
113
|
from reflex.utils.serializers import serializer
|
|
@@ -143,6 +144,9 @@ HANDLED_PICKLE_ERRORS = (
|
|
|
143
144
|
ValueError,
|
|
144
145
|
)
|
|
145
146
|
|
|
147
|
+
# For BaseState.get_var_value
|
|
148
|
+
VAR_TYPE = TypeVar("VAR_TYPE")
|
|
149
|
+
|
|
146
150
|
|
|
147
151
|
def _no_chain_background_task(
|
|
148
152
|
state_cls: Type["BaseState"], name: str, fn: Callable
|
|
@@ -1193,6 +1197,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1193
1197
|
continue
|
|
1194
1198
|
dynamic_vars[param] = DynamicRouteVar(
|
|
1195
1199
|
fget=func,
|
|
1200
|
+
auto_deps=False,
|
|
1201
|
+
deps=["router"],
|
|
1196
1202
|
cache=True,
|
|
1197
1203
|
_js_expr=param,
|
|
1198
1204
|
_var_data=VarData.from_state(cls),
|
|
@@ -1240,13 +1246,16 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1240
1246
|
if not super().__getattribute__("__dict__"):
|
|
1241
1247
|
return super().__getattribute__(name)
|
|
1242
1248
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
}
|
|
1249
|
+
# Fast path for dunder
|
|
1250
|
+
if name.startswith("__"):
|
|
1251
|
+
return super().__getattribute__(name)
|
|
1247
1252
|
|
|
1248
1253
|
# For now, handle router_data updates as a special case.
|
|
1249
|
-
if
|
|
1254
|
+
if (
|
|
1255
|
+
name == constants.ROUTER_DATA
|
|
1256
|
+
or name in super().__getattribute__("inherited_vars")
|
|
1257
|
+
or name in super().__getattribute__("inherited_backend_vars")
|
|
1258
|
+
):
|
|
1250
1259
|
parent_state = super().__getattribute__("parent_state")
|
|
1251
1260
|
if parent_state is not None:
|
|
1252
1261
|
return getattr(parent_state, name)
|
|
@@ -1301,8 +1310,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1301
1310
|
value = value.__wrapped__
|
|
1302
1311
|
|
|
1303
1312
|
# Set the var on the parent state.
|
|
1304
|
-
|
|
1305
|
-
if name in inherited_vars:
|
|
1313
|
+
if name in self.inherited_vars or name in self.inherited_backend_vars:
|
|
1306
1314
|
setattr(self.parent_state, name, value)
|
|
1307
1315
|
return
|
|
1308
1316
|
|
|
@@ -1596,6 +1604,42 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|
|
1596
1604
|
# Slow case - fetch missing parent states from redis.
|
|
1597
1605
|
return await self._get_state_from_redis(state_cls)
|
|
1598
1606
|
|
|
1607
|
+
async def get_var_value(self, var: Var[VAR_TYPE]) -> VAR_TYPE:
|
|
1608
|
+
"""Get the value of an rx.Var from another state.
|
|
1609
|
+
|
|
1610
|
+
Args:
|
|
1611
|
+
var: The var to get the value for.
|
|
1612
|
+
|
|
1613
|
+
Returns:
|
|
1614
|
+
The value of the var.
|
|
1615
|
+
|
|
1616
|
+
Raises:
|
|
1617
|
+
UnretrievableVarValueError: If the var does not have a literal value
|
|
1618
|
+
or associated state.
|
|
1619
|
+
"""
|
|
1620
|
+
# Oopsie case: you didn't give me a Var... so get what you give.
|
|
1621
|
+
if not isinstance(var, Var):
|
|
1622
|
+
return var # type: ignore
|
|
1623
|
+
|
|
1624
|
+
# Fast case: this is a literal var and the value is known.
|
|
1625
|
+
if hasattr(var, "_var_value"):
|
|
1626
|
+
return var._var_value
|
|
1627
|
+
|
|
1628
|
+
var_data = var._get_all_var_data()
|
|
1629
|
+
if var_data is None or not var_data.state:
|
|
1630
|
+
raise UnretrievableVarValueError(
|
|
1631
|
+
f"Unable to retrieve value for {var._js_expr}: not associated with any state."
|
|
1632
|
+
)
|
|
1633
|
+
# Fastish case: this var belongs to this state
|
|
1634
|
+
if var_data.state == self.get_full_name():
|
|
1635
|
+
return getattr(self, var_data.field_name)
|
|
1636
|
+
|
|
1637
|
+
# Slow case: this var belongs to another state
|
|
1638
|
+
other_state = await self.get_state(
|
|
1639
|
+
self._get_root_state().get_class_substate(var_data.state)
|
|
1640
|
+
)
|
|
1641
|
+
return getattr(other_state, var_data.field_name)
|
|
1642
|
+
|
|
1599
1643
|
def _get_event_handler(
|
|
1600
1644
|
self, event: Event
|
|
1601
1645
|
) -> tuple[BaseState | StateProxy, EventHandler]:
|
|
@@ -3645,6 +3689,9 @@ def get_state_manager() -> StateManager:
|
|
|
3645
3689
|
class MutableProxy(wrapt.ObjectProxy):
|
|
3646
3690
|
"""A proxy for a mutable object that tracks changes."""
|
|
3647
3691
|
|
|
3692
|
+
# Hint for finding the base class of the proxy.
|
|
3693
|
+
__base_proxy__ = "MutableProxy"
|
|
3694
|
+
|
|
3648
3695
|
# Methods on wrapped objects which should mark the state as dirty.
|
|
3649
3696
|
__mark_dirty_attrs__ = {
|
|
3650
3697
|
"add",
|
|
@@ -3687,6 +3734,39 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
3687
3734
|
BaseModelV1,
|
|
3688
3735
|
)
|
|
3689
3736
|
|
|
3737
|
+
# Dynamically generated classes for tracking dataclass mutations.
|
|
3738
|
+
__dataclass_proxies__: Dict[type, type] = {}
|
|
3739
|
+
|
|
3740
|
+
def __new__(cls, wrapped: Any, *args, **kwargs) -> MutableProxy:
|
|
3741
|
+
"""Create a proxy instance for a mutable object that tracks changes.
|
|
3742
|
+
|
|
3743
|
+
Args:
|
|
3744
|
+
wrapped: The object to proxy.
|
|
3745
|
+
*args: Other args passed to MutableProxy (ignored).
|
|
3746
|
+
**kwargs: Other kwargs passed to MutableProxy (ignored).
|
|
3747
|
+
|
|
3748
|
+
Returns:
|
|
3749
|
+
The proxy instance.
|
|
3750
|
+
"""
|
|
3751
|
+
if dataclasses.is_dataclass(wrapped):
|
|
3752
|
+
wrapped_cls = type(wrapped)
|
|
3753
|
+
wrapper_cls_name = wrapped_cls.__name__ + cls.__name__
|
|
3754
|
+
# Find the associated class
|
|
3755
|
+
if wrapper_cls_name not in cls.__dataclass_proxies__:
|
|
3756
|
+
# Create a new class that has the __dataclass_fields__ defined
|
|
3757
|
+
cls.__dataclass_proxies__[wrapper_cls_name] = type(
|
|
3758
|
+
wrapper_cls_name,
|
|
3759
|
+
(cls,),
|
|
3760
|
+
{
|
|
3761
|
+
dataclasses._FIELDS: getattr( # pyright: ignore [reportGeneralTypeIssues]
|
|
3762
|
+
wrapped_cls,
|
|
3763
|
+
dataclasses._FIELDS, # pyright: ignore [reportGeneralTypeIssues]
|
|
3764
|
+
),
|
|
3765
|
+
},
|
|
3766
|
+
)
|
|
3767
|
+
cls = cls.__dataclass_proxies__[wrapper_cls_name]
|
|
3768
|
+
return super().__new__(cls)
|
|
3769
|
+
|
|
3690
3770
|
def __init__(self, wrapped: Any, state: BaseState, field_name: str):
|
|
3691
3771
|
"""Create a proxy for a mutable object that tracks changes.
|
|
3692
3772
|
|
|
@@ -3743,7 +3823,27 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
3743
3823
|
Returns:
|
|
3744
3824
|
Whether the value is of a mutable type.
|
|
3745
3825
|
"""
|
|
3746
|
-
return isinstance(value, cls.__mutable_types__)
|
|
3826
|
+
return isinstance(value, cls.__mutable_types__) or (
|
|
3827
|
+
dataclasses.is_dataclass(value) and not isinstance(value, Var)
|
|
3828
|
+
)
|
|
3829
|
+
|
|
3830
|
+
@staticmethod
|
|
3831
|
+
def _is_called_from_dataclasses_internal() -> bool:
|
|
3832
|
+
"""Check if the current function is called from dataclasses helper.
|
|
3833
|
+
|
|
3834
|
+
Returns:
|
|
3835
|
+
Whether the current function is called from dataclasses internal code.
|
|
3836
|
+
"""
|
|
3837
|
+
# Walk up the stack a bit to see if we are called from dataclasses
|
|
3838
|
+
# internal code, for example `asdict` or `astuple`.
|
|
3839
|
+
frame = inspect.currentframe()
|
|
3840
|
+
for _ in range(5):
|
|
3841
|
+
# Why not `inspect.stack()` -- this is much faster!
|
|
3842
|
+
if not (frame := frame and frame.f_back):
|
|
3843
|
+
break
|
|
3844
|
+
if inspect.getfile(frame) == dataclasses.__file__:
|
|
3845
|
+
return True
|
|
3846
|
+
return False
|
|
3747
3847
|
|
|
3748
3848
|
def _wrap_recursive(self, value: Any) -> Any:
|
|
3749
3849
|
"""Wrap a value recursively if it is mutable.
|
|
@@ -3754,9 +3854,13 @@ class MutableProxy(wrapt.ObjectProxy):
|
|
|
3754
3854
|
Returns:
|
|
3755
3855
|
The wrapped value.
|
|
3756
3856
|
"""
|
|
3857
|
+
# When called from dataclasses internal code, return the unwrapped value
|
|
3858
|
+
if self._is_called_from_dataclasses_internal():
|
|
3859
|
+
return value
|
|
3757
3860
|
# Recursively wrap mutable types, but do not re-wrap MutableProxy instances.
|
|
3758
3861
|
if self._is_mutable_type(value) and not isinstance(value, MutableProxy):
|
|
3759
|
-
|
|
3862
|
+
base_cls = globals()[self.__base_proxy__]
|
|
3863
|
+
return base_cls(
|
|
3760
3864
|
wrapped=value,
|
|
3761
3865
|
state=self._self_state,
|
|
3762
3866
|
field_name=self._self_field_name,
|
|
@@ -3964,6 +4068,9 @@ class ImmutableMutableProxy(MutableProxy):
|
|
|
3964
4068
|
to modify the wrapped object when the StateProxy is immutable.
|
|
3965
4069
|
"""
|
|
3966
4070
|
|
|
4071
|
+
# Ensure that recursively wrapped proxies use ImmutableMutableProxy as base.
|
|
4072
|
+
__base_proxy__ = "ImmutableMutableProxy"
|
|
4073
|
+
|
|
3967
4074
|
def _mark_dirty(
|
|
3968
4075
|
self,
|
|
3969
4076
|
wrapped=None,
|
reflex/testing.py
CHANGED
|
@@ -52,6 +52,7 @@ from reflex.state import (
|
|
|
52
52
|
StateManagerRedis,
|
|
53
53
|
reload_state_module,
|
|
54
54
|
)
|
|
55
|
+
from reflex.utils import console
|
|
55
56
|
|
|
56
57
|
try:
|
|
57
58
|
from selenium import webdriver # pyright: ignore [reportMissingImports]
|
|
@@ -385,7 +386,7 @@ class AppHarness:
|
|
|
385
386
|
)
|
|
386
387
|
if not line:
|
|
387
388
|
break
|
|
388
|
-
print(line) # for pytest diagnosis
|
|
389
|
+
print(line) # for pytest diagnosis #noqa: T201
|
|
389
390
|
m = re.search(reflex.constants.Next.FRONTEND_LISTENING_REGEX, line)
|
|
390
391
|
if m is not None:
|
|
391
392
|
self.frontend_url = m.group(1)
|
|
@@ -403,11 +404,10 @@ class AppHarness:
|
|
|
403
404
|
)
|
|
404
405
|
# catch I/O operation on closed file.
|
|
405
406
|
except ValueError as e:
|
|
406
|
-
|
|
407
|
+
console.error(str(e))
|
|
407
408
|
break
|
|
408
409
|
if not line:
|
|
409
410
|
break
|
|
410
|
-
print(line)
|
|
411
411
|
|
|
412
412
|
self.frontend_output_thread = threading.Thread(target=consume_frontend_output)
|
|
413
413
|
self.frontend_output_thread.start()
|
reflex/utils/exceptions.py
CHANGED
|
@@ -187,3 +187,7 @@ def raise_system_package_missing_error(package: str) -> NoReturn:
|
|
|
187
187
|
|
|
188
188
|
class InvalidLockWarningThresholdError(ReflexError):
|
|
189
189
|
"""Raised when an invalid lock warning threshold is provided."""
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class UnretrievableVarValueError(ReflexError):
|
|
193
|
+
"""Raised when the value of a var is not retrievable."""
|
reflex/utils/prerequisites.py
CHANGED
|
@@ -28,8 +28,8 @@ import typer
|
|
|
28
28
|
from alembic.util.exc import CommandError
|
|
29
29
|
from packaging import version
|
|
30
30
|
from redis import Redis as RedisSync
|
|
31
|
-
from redis import exceptions
|
|
32
31
|
from redis.asyncio import Redis
|
|
32
|
+
from redis.exceptions import RedisError
|
|
33
33
|
|
|
34
34
|
from reflex import constants, model
|
|
35
35
|
from reflex.compiler import templates
|
|
@@ -109,7 +109,7 @@ def check_latest_package_version(package_name: str):
|
|
|
109
109
|
console.warn(
|
|
110
110
|
f"Your version ({current_version}) of {package_name} is out of date. Upgrade to {latest_version} with 'pip install {package_name} --upgrade'"
|
|
111
111
|
)
|
|
112
|
-
# Check for
|
|
112
|
+
# Check for deprecated python versions
|
|
113
113
|
_python_version_check()
|
|
114
114
|
except Exception:
|
|
115
115
|
pass
|
|
@@ -333,10 +333,11 @@ def get_redis() -> Redis | None:
|
|
|
333
333
|
Returns:
|
|
334
334
|
The asynchronous redis client.
|
|
335
335
|
"""
|
|
336
|
-
if
|
|
337
|
-
return Redis.from_url(
|
|
338
|
-
|
|
339
|
-
|
|
336
|
+
if (redis_url := parse_redis_url()) is not None:
|
|
337
|
+
return Redis.from_url(
|
|
338
|
+
redis_url,
|
|
339
|
+
retry_on_error=[RedisError],
|
|
340
|
+
)
|
|
340
341
|
return None
|
|
341
342
|
|
|
342
343
|
|
|
@@ -346,14 +347,15 @@ def get_redis_sync() -> RedisSync | None:
|
|
|
346
347
|
Returns:
|
|
347
348
|
The synchronous redis client.
|
|
348
349
|
"""
|
|
349
|
-
if
|
|
350
|
-
return RedisSync.from_url(
|
|
351
|
-
|
|
352
|
-
|
|
350
|
+
if (redis_url := parse_redis_url()) is not None:
|
|
351
|
+
return RedisSync.from_url(
|
|
352
|
+
redis_url,
|
|
353
|
+
retry_on_error=[RedisError],
|
|
354
|
+
)
|
|
353
355
|
return None
|
|
354
356
|
|
|
355
357
|
|
|
356
|
-
def parse_redis_url() -> str |
|
|
358
|
+
def parse_redis_url() -> str | None:
|
|
357
359
|
"""Parse the REDIS_URL in config if applicable.
|
|
358
360
|
|
|
359
361
|
Returns:
|
|
@@ -372,16 +374,13 @@ def parse_redis_url() -> str | dict | None:
|
|
|
372
374
|
return config.redis_url
|
|
373
375
|
|
|
374
376
|
|
|
375
|
-
async def get_redis_status() -> bool | None:
|
|
377
|
+
async def get_redis_status() -> dict[str, bool | None]:
|
|
376
378
|
"""Checks the status of the Redis connection.
|
|
377
379
|
|
|
378
380
|
Attempts to connect to Redis and send a ping command to verify connectivity.
|
|
379
381
|
|
|
380
382
|
Returns:
|
|
381
|
-
|
|
382
|
-
- True: Redis is accessible and responding.
|
|
383
|
-
- False: Redis is not accessible due to a connection error.
|
|
384
|
-
- None: Redis not used i.e redis_url is not set in rxconfig.
|
|
383
|
+
The status of the Redis connection.
|
|
385
384
|
"""
|
|
386
385
|
try:
|
|
387
386
|
status = True
|
|
@@ -390,10 +389,10 @@ async def get_redis_status() -> bool | None:
|
|
|
390
389
|
redis_client.ping()
|
|
391
390
|
else:
|
|
392
391
|
status = None
|
|
393
|
-
except
|
|
392
|
+
except RedisError:
|
|
394
393
|
status = False
|
|
395
394
|
|
|
396
|
-
return status
|
|
395
|
+
return {"redis": status}
|
|
397
396
|
|
|
398
397
|
|
|
399
398
|
def validate_app_name(app_name: str | None = None) -> str:
|
|
@@ -594,7 +593,7 @@ def initialize_web_directory():
|
|
|
594
593
|
"""Initialize the web directory on reflex init."""
|
|
595
594
|
console.log("Initializing the web directory.")
|
|
596
595
|
|
|
597
|
-
#
|
|
596
|
+
# Reuse the hash if one is already created, so we don't over-write it when running reflex init
|
|
598
597
|
project_hash = get_project_hash()
|
|
599
598
|
|
|
600
599
|
path_ops.cp(constants.Templates.Dirs.WEB_TEMPLATE, str(get_web_dir()))
|
|
@@ -647,7 +646,7 @@ def initialize_bun_config():
|
|
|
647
646
|
def init_reflex_json(project_hash: int | None):
|
|
648
647
|
"""Write the hash of the Reflex project to a REFLEX_JSON.
|
|
649
648
|
|
|
650
|
-
|
|
649
|
+
Reuse the hash if one is already created, therefore do not
|
|
651
650
|
overwrite it every time we run the reflex init command
|
|
652
651
|
.
|
|
653
652
|
|
|
@@ -1177,6 +1176,24 @@ def initialize_frontend_dependencies():
|
|
|
1177
1176
|
initialize_web_directory()
|
|
1178
1177
|
|
|
1179
1178
|
|
|
1179
|
+
def check_db_used() -> bool:
|
|
1180
|
+
"""Check if the database is used.
|
|
1181
|
+
|
|
1182
|
+
Returns:
|
|
1183
|
+
True if the database is used.
|
|
1184
|
+
"""
|
|
1185
|
+
return bool(get_config().db_url)
|
|
1186
|
+
|
|
1187
|
+
|
|
1188
|
+
def check_redis_used() -> bool:
|
|
1189
|
+
"""Check if Redis is used.
|
|
1190
|
+
|
|
1191
|
+
Returns:
|
|
1192
|
+
True if Redis is used.
|
|
1193
|
+
"""
|
|
1194
|
+
return bool(get_config().redis_url)
|
|
1195
|
+
|
|
1196
|
+
|
|
1180
1197
|
def check_db_initialized() -> bool:
|
|
1181
1198
|
"""Check if the database migrations are initialized.
|
|
1182
1199
|
|
reflex/utils/processes.py
CHANGED
|
@@ -118,7 +118,7 @@ def handle_port(service_name: str, port: str, default_port: str) -> str:
|
|
|
118
118
|
"""Change port if the specified port is in use and is not explicitly specified as a CLI arg or config arg.
|
|
119
119
|
otherwise tell the user the port is in use and exit the app.
|
|
120
120
|
|
|
121
|
-
We make an assumption that when port is the default port,then it
|
|
121
|
+
We make an assumption that when port is the default port,then it hasn't been explicitly set since its not straightforward
|
|
122
122
|
to know whether a port was explicitly provided by the user unless its any other than the default.
|
|
123
123
|
|
|
124
124
|
Args:
|
|
@@ -351,7 +351,7 @@ def atexit_handler():
|
|
|
351
351
|
|
|
352
352
|
def get_command_with_loglevel(command: list[str]) -> list[str]:
|
|
353
353
|
"""Add the right loglevel flag to the designated command.
|
|
354
|
-
npm uses --loglevel <level>, Bun
|
|
354
|
+
npm uses --loglevel <level>, Bun doesn't use the --loglevel flag and
|
|
355
355
|
runs in debug mode by default.
|
|
356
356
|
|
|
357
357
|
Args:
|
reflex/utils/pyi_generator.py
CHANGED
|
@@ -1023,7 +1023,7 @@ class InitStubGenerator(StubGenerator):
|
|
|
1023
1023
|
|
|
1024
1024
|
class PyiGenerator:
|
|
1025
1025
|
"""A .pyi file generator that will scan all defined Component in Reflex and
|
|
1026
|
-
generate the
|
|
1026
|
+
generate the appropriate stub.
|
|
1027
1027
|
"""
|
|
1028
1028
|
|
|
1029
1029
|
modules: list = []
|
|
@@ -1202,4 +1202,4 @@ class PyiGenerator:
|
|
|
1202
1202
|
or "Var[Template]" in line
|
|
1203
1203
|
):
|
|
1204
1204
|
line = line.rstrip() + " # type: ignore\n"
|
|
1205
|
-
print(line, end="")
|
|
1205
|
+
print(line, end="") # noqa: T201
|
reflex/utils/telemetry.py
CHANGED
|
@@ -7,6 +7,7 @@ import dataclasses
|
|
|
7
7
|
import multiprocessing
|
|
8
8
|
import platform
|
|
9
9
|
import warnings
|
|
10
|
+
from contextlib import suppress
|
|
10
11
|
|
|
11
12
|
from reflex.config import environment
|
|
12
13
|
|
|
@@ -171,10 +172,11 @@ def _send(event, telemetry_enabled, **kwargs):
|
|
|
171
172
|
if not telemetry_enabled:
|
|
172
173
|
return False
|
|
173
174
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
175
|
+
with suppress(Exception):
|
|
176
|
+
event_data = _prepare_event(event, **kwargs)
|
|
177
|
+
if not event_data:
|
|
178
|
+
return False
|
|
179
|
+
return _send_event(event_data)
|
|
178
180
|
|
|
179
181
|
|
|
180
182
|
def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
reflex/vars/base.py
CHANGED
|
@@ -127,7 +127,7 @@ class VarData:
|
|
|
127
127
|
state: str = "",
|
|
128
128
|
field_name: str = "",
|
|
129
129
|
imports: ImportDict | ParsedImportDict | None = None,
|
|
130
|
-
hooks: dict[str, None] | None = None,
|
|
130
|
+
hooks: dict[str, VarData | None] | None = None,
|
|
131
131
|
deps: list[Var] | None = None,
|
|
132
132
|
position: Hooks.HookPosition | None = None,
|
|
133
133
|
):
|
|
@@ -194,7 +194,9 @@ class VarData:
|
|
|
194
194
|
(var_data.state for var_data in all_var_datas if var_data.state), ""
|
|
195
195
|
)
|
|
196
196
|
|
|
197
|
-
hooks
|
|
197
|
+
hooks: dict[str, VarData | None] = {
|
|
198
|
+
hook: None for var_data in all_var_datas for hook in var_data.hooks
|
|
199
|
+
}
|
|
198
200
|
|
|
199
201
|
_imports = imports.merge_imports(
|
|
200
202
|
*(var_data.imports for var_data in all_var_datas)
|
|
@@ -579,7 +581,7 @@ class Var(Generic[VAR_TYPE]):
|
|
|
579
581
|
|
|
580
582
|
# Try to pull the imports and hooks from contained values.
|
|
581
583
|
if not isinstance(value, str):
|
|
582
|
-
return LiteralVar.create(value)
|
|
584
|
+
return LiteralVar.create(value, _var_data=_var_data)
|
|
583
585
|
|
|
584
586
|
if _var_is_string is False or _var_is_local is True:
|
|
585
587
|
return cls(
|
|
@@ -2276,7 +2278,7 @@ def computed_var(
|
|
|
2276
2278
|
def computed_var(
|
|
2277
2279
|
fget: Callable[[BASE_STATE], Any] | None = None,
|
|
2278
2280
|
initial_value: Any | types.Unset = types.Unset(),
|
|
2279
|
-
cache: bool =
|
|
2281
|
+
cache: Optional[bool] = None,
|
|
2280
2282
|
deps: Optional[List[Union[str, Var]]] = None,
|
|
2281
2283
|
auto_deps: bool = True,
|
|
2282
2284
|
interval: Optional[Union[datetime.timedelta, int]] = None,
|
|
@@ -2302,6 +2304,15 @@ def computed_var(
|
|
|
2302
2304
|
ValueError: If caching is disabled and an update interval is set.
|
|
2303
2305
|
VarDependencyError: If user supplies dependencies without caching.
|
|
2304
2306
|
"""
|
|
2307
|
+
if cache is None:
|
|
2308
|
+
cache = False
|
|
2309
|
+
console.deprecate(
|
|
2310
|
+
"Default non-cached rx.var",
|
|
2311
|
+
"the default value will be `@rx.var(cache=True)` in a future release. "
|
|
2312
|
+
"To retain uncached var, explicitly pass `@rx.var(cache=False)`",
|
|
2313
|
+
deprecation_version="0.6.8",
|
|
2314
|
+
removal_version="0.7.0",
|
|
2315
|
+
)
|
|
2305
2316
|
if cache is False and interval is not None:
|
|
2306
2317
|
raise ValueError("Cannot set update interval without caching.")
|
|
2307
2318
|
|
reflex/vars/sequence.py
CHANGED
|
@@ -271,6 +271,25 @@ class StringVar(Var[STRING_TYPE], python_types=str):
|
|
|
271
271
|
raise_unsupported_operand_types("startswith", (type(self), type(prefix)))
|
|
272
272
|
return string_starts_with_operation(self, prefix)
|
|
273
273
|
|
|
274
|
+
@overload
|
|
275
|
+
def endswith(self, suffix: StringVar | str) -> BooleanVar: ...
|
|
276
|
+
|
|
277
|
+
@overload
|
|
278
|
+
def endswith(self, suffix: NoReturn) -> NoReturn: ...
|
|
279
|
+
|
|
280
|
+
def endswith(self, suffix: Any) -> BooleanVar:
|
|
281
|
+
"""Check if the string ends with a suffix.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
suffix: The suffix.
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
The string ends with operation.
|
|
288
|
+
"""
|
|
289
|
+
if not isinstance(suffix, (StringVar, str)):
|
|
290
|
+
raise_unsupported_operand_types("endswith", (type(self), type(suffix)))
|
|
291
|
+
return string_ends_with_operation(self, suffix)
|
|
292
|
+
|
|
274
293
|
@overload
|
|
275
294
|
def __lt__(self, other: StringVar | str) -> BooleanVar: ...
|
|
276
295
|
|
|
@@ -501,6 +520,24 @@ def string_starts_with_operation(
|
|
|
501
520
|
)
|
|
502
521
|
|
|
503
522
|
|
|
523
|
+
@var_operation
|
|
524
|
+
def string_ends_with_operation(
|
|
525
|
+
full_string: StringVar[Any], suffix: StringVar[Any] | str
|
|
526
|
+
):
|
|
527
|
+
"""Check if a string ends with a suffix.
|
|
528
|
+
|
|
529
|
+
Args:
|
|
530
|
+
full_string: The full string.
|
|
531
|
+
suffix: The suffix.
|
|
532
|
+
|
|
533
|
+
Returns:
|
|
534
|
+
Whether the string ends with the suffix.
|
|
535
|
+
"""
|
|
536
|
+
return var_operation_return(
|
|
537
|
+
js_expression=f"{full_string}.endsWith({suffix})", var_type=bool
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
|
|
504
541
|
@var_operation
|
|
505
542
|
def string_item_operation(string: StringVar[Any], index: NumberVar | int):
|
|
506
543
|
"""Get an item from a string.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: reflex
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.8
|
|
4
4
|
Summary: Web apps in pure Python.
|
|
5
5
|
Home-page: https://reflex.dev
|
|
6
6
|
License: Apache-2.0
|
|
@@ -301,7 +301,7 @@ We welcome contributions of any size! Below are some good ways to get started in
|
|
|
301
301
|
- **GitHub Discussions**: A great way to talk about features you want added or things that are confusing/need clarification.
|
|
302
302
|
- **GitHub Issues**: [Issues](https://github.com/reflex-dev/reflex/issues) are an excellent way to report bugs. Additionally, you can try and solve an existing issue and submit a PR.
|
|
303
303
|
|
|
304
|
-
We are actively looking for contributors, no matter your skill level or experience. To contribute check out [
|
|
304
|
+
We are actively looking for contributors, no matter your skill level or experience. To contribute check out [CONTRIBUTING.md](https://github.com/reflex-dev/reflex/blob/main/CONTRIBUTING.md)
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
## All Thanks To Our Contributors:
|