Compare commits

...

7 Commits

Author SHA1 Message Date
Masen Furer
e471dc199a
bump to 0.6.4 release 2024-10-29 16:38:37 -07:00
Masen Furer
00e0c44b84
bump to 0.6.4a3 2024-10-29 13:07:51 -07:00
Khaleel Al-Adhami
6be3a819b7
add existing path subclass for env checks (#4260)
* add existing path subclass for env checks

* use the power of dataclasses

* use metaclass

* fake the class name

* use annotated

* use flag instead of dict

* dang it darglint

* cleanups
2024-10-29 13:06:58 -07:00
benedikt-bartscher
2e100e38d9
port enum env var support from #4248 (#4251)
* port enum env var support from #4248

* add some tests for interpret env var functions
2024-10-29 13:06:58 -07:00
Masen Furer
cdbe7f820b
bump to 0.6.4a2 2024-10-28 17:00:24 -07:00
Masen Furer
5a5be8162b
Handle props annotated as list/dict of EventHandler (#4257)
These props are NOT event triggers themselves, but rather they are props that
expect a list or dict of event handlers.

Additional fix for calling an `@rx.event` decorated event handler with no
arguments.
2024-10-28 17:00:13 -07:00
Masen Furer
85cae46ed3
bump to 0.6.4a1 2024-10-25 09:24:16 -07:00
5 changed files with 84 additions and 13 deletions

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.6.4dev1"
version = "0.6.4"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [
@ -104,4 +104,4 @@ lint.pydocstyle.convention = "google"
[tool.pytest.ini_options]
asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto"
asyncio_mode = "auto"

View File

@ -645,7 +645,7 @@ class Component(BaseComponent, ABC):
# Look for component specific triggers,
# e.g. variable declared as EventHandler types.
for field in self.get_fields().values():
if types._issubclass(field.type_, EventHandler):
if types._issubclass(field.outer_type_, EventHandler):
args_spec = None
annotation = field.annotation
if (metadata := getattr(annotation, "__metadata__", None)) is not None:

View File

@ -3,14 +3,16 @@
from __future__ import annotations
import dataclasses
import enum
import importlib
import inspect
import os
import sys
import urllib.parse
from pathlib import Path
from typing import Any, Dict, List, Optional, Set
from typing_extensions import get_type_hints
from typing_extensions import Annotated, get_type_hints
from reflex.utils.exceptions import ConfigError, EnvironmentVarValueError
from reflex.utils.types import GenericType, is_union, value_inside_optional
@ -202,8 +204,8 @@ def interpret_int_env(value: str, field_name: str) -> int:
) from ve
def interpret_path_env(value: str, field_name: str) -> Path:
"""Interpret a path environment variable value.
def interpret_existing_path_env(value: str, field_name: str) -> ExistingPath:
"""Interpret a path environment variable value as an existing path.
Args:
value: The environment variable value.
@ -221,6 +223,41 @@ def interpret_path_env(value: str, field_name: str) -> Path:
return path
def interpret_path_env(value: str, field_name: str) -> Path:
"""Interpret a path environment variable value.
Args:
value: The environment variable value.
field_name: The field name.
Returns:
The interpreted value.
"""
return Path(value)
def interpret_enum_env(value: str, field_type: GenericType, field_name: str) -> Any:
"""Interpret an enum environment variable value.
Args:
value: The environment variable value.
field_type: The field type.
field_name: The field name.
Returns:
The interpreted value.
Raises:
EnvironmentVarValueError: If the value is invalid.
"""
try:
return field_type(value)
except ValueError as ve:
raise EnvironmentVarValueError(
f"Invalid enum value: {value} for {field_name}"
) from ve
def interpret_env_var_value(
value: str, field_type: GenericType, field_name: str
) -> Any:
@ -252,6 +289,10 @@ def interpret_env_var_value(
return interpret_int_env(value, field_name)
elif field_type is Path:
return interpret_path_env(value, field_name)
elif field_type is ExistingPath:
return interpret_existing_path_env(value, field_name)
elif inspect.isclass(field_type) and issubclass(field_type, enum.Enum):
return interpret_enum_env(value, field_type, field_name)
else:
raise ValueError(
@ -259,6 +300,13 @@ def interpret_env_var_value(
)
class PathExistsFlag:
"""Flag to indicate that a path must exist."""
ExistingPath = Annotated[Path, PathExistsFlag]
@dataclasses.dataclass(init=False)
class EnvironmentVariables:
"""Environment variables class to instantiate environment variables."""
@ -288,7 +336,7 @@ class EnvironmentVariables:
REFLEX_WEB_WORKDIR: Path = Path(constants.Dirs.WEB)
# Path to the alembic config file
ALEMBIC_CONFIG: Path = Path(constants.ALEMBIC_CONFIG)
ALEMBIC_CONFIG: ExistingPath = Path(constants.ALEMBIC_CONFIG)
# Disable SSL verification for HTTPX requests.
SSL_NO_VERIFY: bool = False
@ -401,7 +449,7 @@ class Config(Base):
telemetry_enabled: bool = True
# The bun path
bun_path: Path = constants.Bun.DEFAULT_PATH
bun_path: ExistingPath = constants.Bun.DEFAULT_PATH
# List of origins that are allowed to connect to the backend API.
cors_allowed_origins: List[str] = ["*"]
@ -525,7 +573,7 @@ class Config(Base):
)
# Interpret the value.
value = interpret_env_var_value(env_var, field.type_, field.name)
value = interpret_env_var_value(env_var, field.outer_type_, field.name)
# Set the value.
updated_values[key] = value

View File

@ -1489,6 +1489,11 @@ if sys.version_info >= (3, 10):
"""
return self
@overload
def __call__(
self: EventCallback[Q, T],
) -> EventCallback[Q, T]: ...
@overload
def __call__(
self: EventCallback[Concatenate[V, Q], T], value: V | Var[V]

View File

@ -7,8 +7,13 @@ import pytest
import reflex as rx
import reflex.config
from reflex.config import environment
from reflex.constants import Endpoint
from reflex.config import (
environment,
interpret_boolean_env,
interpret_enum_env,
interpret_int_env,
)
from reflex.constants import Endpoint, Env
def test_requires_app_name():
@ -208,11 +213,11 @@ def test_replace_defaults(
assert getattr(c, key) == value
def reflex_dir_constant():
def reflex_dir_constant() -> Path:
return environment.REFLEX_DIR
def test_reflex_dir_env_var(monkeypatch, tmp_path):
def test_reflex_dir_env_var(monkeypatch: pytest.MonkeyPatch, tmp_path: Path) -> None:
"""Test that the REFLEX_DIR environment variable is used to set the Reflex.DIR constant.
Args:
@ -224,3 +229,16 @@ def test_reflex_dir_env_var(monkeypatch, tmp_path):
mp_ctx = multiprocessing.get_context(method="spawn")
with mp_ctx.Pool(processes=1) as pool:
assert pool.apply(reflex_dir_constant) == tmp_path
def test_interpret_enum_env() -> None:
assert interpret_enum_env(Env.PROD.value, Env, "REFLEX_ENV") == Env.PROD
def test_interpret_int_env() -> None:
assert interpret_int_env("3001", "FRONTEND_PORT") == 3001
@pytest.mark.parametrize("value, expected", [("true", True), ("false", False)])
def test_interpret_bool_env(value: str, expected: bool) -> None:
assert interpret_boolean_env(value, "TELEMETRY_ENABLED") == expected