Compare commits

...

4 Commits
main ... v0.5.1

Author SHA1 Message Date
Masen Furer
4cfa0e3b71
Bump to 0.5.1 (#3364) 2024-05-21 17:18:57 -07:00
Nikhil Rao
9a25bdfd1b
Suppress runtime warnings (#3354) 2024-05-21 12:40:37 -07:00
Nikhil Rao
83a4e547d2
Catch more errors in frontend/backend (#3346) 2024-05-21 12:40:36 -07:00
benedikt-bartscher
69a09f5484
fix rx.cond with ComputedVars and use union type (#3336) 2024-05-20 10:47:52 -07:00
9 changed files with 89 additions and 31 deletions

View File

@ -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 = [

View File

@ -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

View File

@ -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

View File

@ -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),

View File

@ -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."),

View File

@ -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

View File

@ -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)

View File

@ -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]

View File

@ -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")