Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4cfa0e3b71 | ||
![]() |
9a25bdfd1b | ||
![]() |
83a4e547d2 | ||
![]() |
69a09f5484 |
@ -1,6 +1,6 @@
|
||||
[tool.poetry]
|
||||
name = "reflex"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
description = "Web apps in pure Python."
|
||||
license = "Apache-2.0"
|
||||
authors = [
|
||||
|
@ -1068,13 +1068,12 @@ async def process(
|
||||
client_ip: The client_ip.
|
||||
|
||||
Raises:
|
||||
ReflexError: If a reflex specific error occurs during processing the event.
|
||||
Exception: If a reflex specific error occurs during processing the event.
|
||||
|
||||
Yields:
|
||||
The state updates after processing the event.
|
||||
"""
|
||||
from reflex.utils import telemetry
|
||||
from reflex.utils.exceptions import ReflexError
|
||||
|
||||
try:
|
||||
# Add request data to the state.
|
||||
@ -1118,8 +1117,8 @@ async def process(
|
||||
|
||||
# Yield the update.
|
||||
yield update
|
||||
except ReflexError as ex:
|
||||
telemetry.send("error", context="backend", detail=str(ex))
|
||||
except Exception as ex:
|
||||
telemetry.send_error(ex, context="backend")
|
||||
raise
|
||||
|
||||
|
||||
|
@ -4,12 +4,14 @@ Only the app attribute is explicitly exposed.
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from reflex import constants
|
||||
from reflex.utils import telemetry
|
||||
from reflex.utils.exec import is_prod_mode
|
||||
from reflex.utils.prerequisites import get_app
|
||||
|
||||
if "app" != constants.CompileVars.APP:
|
||||
raise AssertionError("unexpected variable name for 'app'")
|
||||
|
||||
telemetry.send("compile")
|
||||
app_module = get_app(reload=False)
|
||||
app = getattr(app_module, constants.CompileVars.APP)
|
||||
# For py3.8 and py3.9 compatibility when redis is used, we MUST add any decorator pages
|
||||
@ -29,5 +31,6 @@ del app_module
|
||||
del compile_future
|
||||
del get_app
|
||||
del is_prod_mode
|
||||
del telemetry
|
||||
del constants
|
||||
del ThreadPoolExecutor
|
||||
|
@ -1,7 +1,8 @@
|
||||
"""Create a list of components from an iterable."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Dict, Optional, overload
|
||||
from typing import Any, Dict, Optional, Union, overload
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.component import BaseComponent, Component, MemoizationLeaf
|
||||
@ -10,7 +11,7 @@ from reflex.constants import Dirs
|
||||
from reflex.constants.colors import Color
|
||||
from reflex.style import LIGHT_COLOR_MODE, color_mode
|
||||
from reflex.utils import format, imports
|
||||
from reflex.vars import BaseVar, Var, VarData
|
||||
from reflex.vars import Var, VarData
|
||||
|
||||
_IS_TRUE_IMPORT = {
|
||||
f"/{Dirs.STATE_PATH}": [imports.ImportVar(tag="isTrue")],
|
||||
@ -171,6 +172,11 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
c2 = create_var(c2)
|
||||
var_datas.extend([c1._var_data, c2._var_data])
|
||||
|
||||
c1_type = c1._var_type if isinstance(c1, Var) else type(c1)
|
||||
c2_type = c2._var_type if isinstance(c2, Var) else type(c2)
|
||||
|
||||
var_type = c1_type if c1_type == c2_type else Union[c1_type, c2_type]
|
||||
|
||||
# Create the conditional var.
|
||||
return cond_var._replace(
|
||||
_var_name=format.format_cond(
|
||||
@ -179,7 +185,7 @@ def cond(condition: Any, c1: Any, c2: Any = None):
|
||||
false_value=c2,
|
||||
is_prop=True,
|
||||
),
|
||||
_var_type=c1._var_type if isinstance(c1, BaseVar) else type(c1),
|
||||
_var_type=var_type,
|
||||
_var_is_local=False,
|
||||
_var_full_name_needs_state_prefix=False,
|
||||
merge_var_data=VarData.merge(*var_datas),
|
||||
|
@ -1476,7 +1476,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
StateUpdate object
|
||||
"""
|
||||
from reflex.utils import telemetry
|
||||
from reflex.utils.exceptions import ReflexError
|
||||
|
||||
# Get the function to process the event.
|
||||
fn = functools.partial(handler.fn, state)
|
||||
@ -1516,8 +1515,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
except Exception as ex:
|
||||
error = traceback.format_exc()
|
||||
print(error)
|
||||
if isinstance(ex, ReflexError):
|
||||
telemetry.send("error", context="backend", detail=str(ex))
|
||||
telemetry.send_error(ex, context="backend")
|
||||
yield state._as_state_update(
|
||||
handler,
|
||||
window_alert("An error occurred. See logs for details."),
|
||||
|
@ -233,9 +233,8 @@ def get_app(reload: bool = False) -> ModuleType:
|
||||
|
||||
Raises:
|
||||
RuntimeError: If the app name is not set in the config.
|
||||
exceptions.ReflexError: Reflex specific errors.
|
||||
"""
|
||||
from reflex.utils import exceptions, telemetry
|
||||
from reflex.utils import telemetry
|
||||
|
||||
try:
|
||||
os.environ[constants.RELOAD_CONFIG] = str(reload)
|
||||
@ -259,8 +258,8 @@ def get_app(reload: bool = False) -> ModuleType:
|
||||
importlib.reload(app)
|
||||
|
||||
return app
|
||||
except exceptions.ReflexError as ex:
|
||||
telemetry.send("error", context="frontend", detail=str(ex))
|
||||
except Exception as ex:
|
||||
telemetry.send_error(ex, context="frontend")
|
||||
raise
|
||||
|
||||
|
||||
|
@ -2,8 +2,10 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import multiprocessing
|
||||
import platform
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from datetime import UTC, datetime
|
||||
@ -157,17 +159,7 @@ def _send_event(event_data: dict) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def send(event: str, telemetry_enabled: bool | None = None, **kwargs) -> bool:
|
||||
"""Send anonymous telemetry for Reflex.
|
||||
|
||||
Args:
|
||||
event: The event name.
|
||||
telemetry_enabled: Whether to send the telemetry (If None, get from config).
|
||||
kwargs: Additional data to send with the event.
|
||||
|
||||
Returns:
|
||||
Whether the telemetry was sent successfully.
|
||||
"""
|
||||
def _send(event, telemetry_enabled, **kwargs):
|
||||
from reflex.config import get_config
|
||||
|
||||
# Get the telemetry_enabled from the config if it is not specified.
|
||||
@ -182,3 +174,37 @@ def send(event: str, telemetry_enabled: bool | None = None, **kwargs) -> bool:
|
||||
if not event_data:
|
||||
return False
|
||||
return _send_event(event_data)
|
||||
|
||||
|
||||
def send(event: str, telemetry_enabled: bool | None = None, **kwargs):
|
||||
"""Send anonymous telemetry for Reflex.
|
||||
|
||||
Args:
|
||||
event: The event name.
|
||||
telemetry_enabled: Whether to send the telemetry (If None, get from config).
|
||||
kwargs: Additional data to send with the event.
|
||||
"""
|
||||
|
||||
async def async_send(event, telemetry_enabled, **kwargs):
|
||||
return _send(event, telemetry_enabled, **kwargs)
|
||||
|
||||
try:
|
||||
# Within an event loop context, send the event asynchronously.
|
||||
asyncio.create_task(async_send(event, telemetry_enabled, **kwargs))
|
||||
except RuntimeError:
|
||||
# If there is no event loop, send the event synchronously.
|
||||
warnings.filterwarnings("ignore", category=RuntimeWarning)
|
||||
_send(event, telemetry_enabled, **kwargs)
|
||||
|
||||
|
||||
def send_error(error: Exception, context: str):
|
||||
"""Send an error event.
|
||||
|
||||
Args:
|
||||
error: The error to send.
|
||||
context: The context of the error (e.g. "frontend" or "backend")
|
||||
|
||||
Returns:
|
||||
Whether the telemetry was sent successfully.
|
||||
"""
|
||||
return send("error", detail=type(error).__name__, context=context)
|
||||
|
@ -1,13 +1,14 @@
|
||||
import json
|
||||
from typing import Any
|
||||
from typing import Any, Union
|
||||
|
||||
import pytest
|
||||
|
||||
from reflex.components.base.fragment import Fragment
|
||||
from reflex.components.core.cond import Cond, cond
|
||||
from reflex.components.radix.themes.typography.text import Text
|
||||
from reflex.state import BaseState
|
||||
from reflex.vars import Var
|
||||
from reflex.state import BaseState, State
|
||||
from reflex.utils.format import format_state_name
|
||||
from reflex.vars import BaseVar, Var, computed_var
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -118,3 +119,29 @@ def test_cond_no_else():
|
||||
# Props do not support the use of cond without else
|
||||
with pytest.raises(ValueError):
|
||||
cond(True, "hello") # type: ignore
|
||||
|
||||
|
||||
def test_cond_computed_var():
|
||||
"""Test if cond works with computed vars."""
|
||||
|
||||
class CondStateComputed(State):
|
||||
@computed_var
|
||||
def computed_int(self) -> int:
|
||||
return 0
|
||||
|
||||
@computed_var
|
||||
def computed_str(self) -> str:
|
||||
return "a string"
|
||||
|
||||
comp = cond(True, CondStateComputed.computed_int, CondStateComputed.computed_str)
|
||||
|
||||
# TODO: shouln't this be a ComputedVar?
|
||||
assert isinstance(comp, BaseVar)
|
||||
|
||||
state_name = format_state_name(CondStateComputed.get_full_name())
|
||||
assert (
|
||||
str(comp)
|
||||
== f"{{isTrue(true) ? {state_name}.computed_int : {state_name}.computed_str}}"
|
||||
)
|
||||
|
||||
assert comp._var_type == Union[int, str]
|
||||
|
@ -29,7 +29,7 @@ def test_telemetry():
|
||||
|
||||
def test_disable():
|
||||
"""Test that disabling telemetry works."""
|
||||
assert not telemetry.send("test", telemetry_enabled=False)
|
||||
assert not telemetry._send("test", telemetry_enabled=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("event", ["init", "reinit", "run-dev", "run-prod", "export"])
|
||||
@ -43,7 +43,7 @@ def test_send(mocker, event):
|
||||
)
|
||||
mocker.patch("platform.platform", return_value="Mocked Platform")
|
||||
|
||||
telemetry.send(event, telemetry_enabled=True)
|
||||
telemetry._send(event, telemetry_enabled=True)
|
||||
httpx.post.assert_called_once()
|
||||
if telemetry.get_os() == "Windows":
|
||||
open.assert_called_with(".web\\reflex.json", "r")
|
||||
|
Loading…
Reference in New Issue
Block a user