remove deprecated features and support for py3.9

This commit is contained in:
Lendemor 2025-01-03 17:17:32 +01:00
parent 12eaf08c88
commit 7cc78cd8ce
36 changed files with 85 additions and 338 deletions

View File

@ -81,15 +81,11 @@ jobs:
matrix:
# Show OS combos first in GUI
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
python-version: ['3.10.13', '3.11.5', '3.12.0']
exclude:
- os: windows-latest
python-version: '3.10.13'
- os: windows-latest
python-version: '3.9.18'
# keep only one python version for MacOS
- os: macos-latest
python-version: '3.9.18'
- os: macos-latest
python-version: '3.10.13'
- os: macos-latest
@ -97,8 +93,6 @@ jobs:
include:
- os: windows-latest
python-version: '3.10.11'
- os: windows-latest
python-version: '3.9.13'
runs-on: ${{ matrix.os }}
steps:

View File

@ -16,7 +16,7 @@ jobs:
- uses: ./.github/actions/setup_build_env
with:
python-version: '3.9'
python-version: '3.10'
run-poetry-install: true
create-venv-at-path: .venv

View File

@ -43,17 +43,13 @@ jobs:
matrix:
# Show OS combos first in GUI
os: [ubuntu-latest, windows-latest]
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0', '3.13.0']
python-version: ['3.10.13', '3.11.5', '3.12.0', '3.13.0']
exclude:
- os: windows-latest
python-version: '3.10.13'
- os: windows-latest
python-version: '3.9.18'
include:
- os: windows-latest
python-version: '3.10.11'
- os: windows-latest
python-version: '3.9.13'
runs-on: ${{ matrix.os }}
steps:

View File

@ -28,18 +28,14 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0', '3.13.0']
python-version: ['3.10.13', '3.11.5', '3.12.0', '3.13.0']
# Windows is a bit behind on Python version availability in Github
exclude:
- os: windows-latest
python-version: '3.10.13'
- os: windows-latest
python-version: '3.9.18'
include:
- os: windows-latest
python-version: '3.10.11'
- os: windows-latest
python-version: '3.9.13'
runs-on: ${{ matrix.os }}
# Service containers to run with `runner-job`
@ -88,8 +84,8 @@ jobs:
strategy:
fail-fast: false
matrix:
# Note: py39, py310 versions chosen due to available arm64 darwin builds.
python-version: ['3.9.13', '3.10.11', '3.11.5', '3.12.0', '3.13.0']
# Note: py310 version chosen due to available arm64 darwin builds.
python-version: ['3.10.11', '3.11.5', '3.12.0', '3.13.0']
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

View File

@ -8,7 +8,7 @@ Here is a quick guide on how to run Reflex repo locally so you can start contrib
**Prerequisites:**
- Python >= 3.9
- Python >= 3.10
- Poetry version >= 1.4.0 and add it to your path (see [Poetry Docs](https://python-poetry.org/docs/#installation) for more info).
**1. Fork this repository:**
@ -87,7 +87,7 @@ poetry run ruff format .
```
Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit.
Note that pre-commit will only be installed when you use a Python version >= 3.9.
Note that pre-commit will only be installed when you use a Python version >= 3.10.
``` bash
pre-commit install

View File

@ -34,7 +34,7 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
## ⚙️ Installation
Open a terminal and run (Requires Python 3.9+):
Open a terminal and run (Requires Python 3.10+):
```bash
pip install reflex

View File

@ -34,7 +34,7 @@ Auf unserer [Architektur-Seite](https://reflex.dev/blog/2024-03-21-reflex-archit
## ⚙️ Installation
Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.9+):
Öffne ein Terminal und führe den folgenden Befehl aus (benötigt Python 3.10+):
```bash
pip install reflex

View File

@ -35,7 +35,7 @@ Consulta nuestra [página de arquitectura](https://reflex.dev/blog/2024-03-21-re
## ⚙️ Instalación
Abra un terminal y ejecute (Requiere Python 3.9+):
Abra un terminal y ejecute (Requiere Python 3.10+):
```bash
pip install reflex

View File

@ -35,7 +35,7 @@ Reflex के अंदर के कामकाज को जानने क
## ⚙️ इंस्टॉलेशन (Installation)
एक टर्मिनल खोलें और चलाएं (Python 3.9+ की आवश्यकता है):
एक टर्मिनल खोलें और चलाएं (Python 3.10+ की आवश्यकता है):
```bash
pip install reflex

View File

@ -22,7 +22,7 @@
## ⚙️ Installazione
Apri un terminale ed esegui (Richiede Python 3.9+):
Apri un terminale ed esegui (Richiede Python 3.10+):
```bash
pip install reflex

View File

@ -37,7 +37,7 @@ Reflex がどのように動作しているかを知るには、[アーキテク
## ⚙️ インストール
ターミナルを開いて以下のコマンドを実行してください。Python 3.9 以上が必要です。):
ターミナルを開いて以下のコマンドを実行してください。Python 3.10 以上が必要です。):
```bash
pip install reflex

View File

@ -20,7 +20,7 @@
---
## ⚙️ 설치
터미널을 열고 실행하세요. (Python 3.9+ 필요):
터미널을 열고 실행하세요. (Python 3.10+ 필요):
```bash
pip install reflex

View File

@ -34,7 +34,7 @@
## ⚙️ Installation - نصب و راه اندازی
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.9+):
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.10+):
```bash
pip install reflex

View File

@ -21,7 +21,7 @@
---
## ⚙️ Instalação
Abra um terminal e execute (Requer Python 3.9+):
Abra um terminal e execute (Requer Python 3.10+):
```bash
pip install reflex

View File

@ -24,7 +24,7 @@
## ⚙️ Kurulum
Bir terminal açın ve çalıştırın (Python 3.9+ gerekir):
Bir terminal açın ve çalıştırın (Python 3.10+ gerekir):
```bash
pip install reflex

View File

@ -34,7 +34,7 @@ Các tính năng chính:
## ⚙️ Cài đặt
Mở cửa sổ lệnh và chạy (Yêu cầu Python phiên bản 3.9+):
Mở cửa sổ lệnh và chạy (Yêu cầu Python phiên bản 3.10+):
```bash
pip install reflex

View File

@ -34,7 +34,7 @@ Reflex 是一个使用纯Python构建全栈web应用的库。
## ⚙️ 安装
打开一个终端并且运行(要求Python3.9+):
打开一个终端并且运行(要求Python3.10+):
```bash
pip install reflex

View File

@ -36,7 +36,7 @@ Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫
## ⚙️ 安裝
開啟一個終端機並且執行 (需要 Python 3.9+):
開啟一個終端機並且執行 (需要 Python 3.10+):
```bash
pip install reflex

View File

@ -19,7 +19,7 @@ classifiers = ["Development Status :: 4 - Beta"]
packages = [{ include = "reflex" }]
[tool.poetry.dependencies]
python = "^3.9"
python = "^3.10"
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
gunicorn = ">=20.1.0,<24.0"
jinja2 = ">=3.1.2,<4.0"

View File

@ -8,7 +8,7 @@ version = "0.0.1"
description = "Reflex custom component {{ module_name }}"
readme = "README.md"
license = { text = "Apache-2.0" }
requires-python = ">=3.9"
requires-python = ">=3.10"
authors = [{ name = "", email = "YOUREMAIL@domain.com" }]
keywords = ["reflex","reflex-custom-components"]

View File

@ -367,19 +367,4 @@ getattr, __dir__, __all__ = lazy_loader.attach(
def __getattr__(name):
if name == "chakra":
from reflex.utils import console
console.deprecate(
"rx.chakra",
reason="and moved to a separate package. "
"To continue using Chakra UI components, install the `reflex-chakra` package via `pip install "
"reflex-chakra`.",
deprecation_version="0.6.0",
removal_version="0.7.0",
dedupe=True,
)
import reflex_chakra as rc
return rc
return getattr(name)

View File

@ -23,8 +23,6 @@ from typing import (
Union,
)
from typing_extensions import deprecated
import reflex.state
from reflex.base import Base
from reflex.compiler.templates import STATEFUL_COMPONENT
@ -47,11 +45,10 @@ from reflex.event import (
EventChain,
EventHandler,
EventSpec,
EventVar,
no_args_event_spec,
)
from reflex.style import Style, format_as_emotion
from reflex.utils import console, format, imports, types
from reflex.utils import format, imports, types
from reflex.utils.imports import (
ImmutableParsedImportDict,
ImportDict,
@ -545,41 +542,6 @@ class Component(BaseComponent, ABC):
# Construct the component.
super().__init__(*args, **kwargs)
@deprecated("Use rx.EventChain.create instead.")
def _create_event_chain(
self,
args_spec: types.ArgsSpec | Sequence[types.ArgsSpec],
value: Union[
Var,
EventHandler,
EventSpec,
List[Union[EventHandler, EventSpec, EventVar]],
Callable,
],
key: Optional[str] = None,
) -> Union[EventChain, Var]:
"""Create an event chain from a variety of input types.
Args:
args_spec: The args_spec of the event trigger being bound.
value: The value to create the event chain from.
key: The key of the event trigger being bound.
Returns:
The event chain.
"""
console.deprecate(
"Component._create_event_chain",
"Use rx.EventChain.create instead.",
deprecation_version="0.6.8",
removal_version="0.7.0",
)
return EventChain.create(
value=value, # type: ignore
args_spec=args_spec,
key=key,
)
def get_event_triggers(
self,
) -> Dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]:

View File

@ -14,7 +14,7 @@ from reflex.components.radix.themes.layout.box import Box
from reflex.constants.colors import Color
from reflex.event import set_clipboard
from reflex.style import Style
from reflex.utils import console, format
from reflex.utils import format
from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var, VarData
@ -438,6 +438,8 @@ class CodeBlock(Component, MarkdownComponentMap):
can_copy = props.pop("can_copy", False)
copy_button = props.pop("copy_button", None)
# react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark
# themes respectively to ensure code compatibility.
if "theme" not in props:
# Default color scheme responds to global color mode.
props["theme"] = color_mode_cond(
@ -445,17 +447,6 @@ class CodeBlock(Component, MarkdownComponentMap):
dark=Theme.one_dark,
)
# react-syntax-highlighter doesn't have an explicit "light" or "dark" theme so we use one-light and one-dark
# themes respectively to ensure code compatibility.
if "theme" in props and not isinstance(props["theme"], Var):
props["theme"] = getattr(Theme, format.to_snake_case(props["theme"])) # type: ignore
console.deprecate(
feature_name="theme prop as string",
reason="Use code_block.themes instead.",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
if can_copy:
code = children[0]
copy_button = ( # type: ignore

View File

@ -40,7 +40,11 @@ from typing_extensions import (
from reflex import constants
from reflex.constants.state import FRONTEND_EVENT_STATE
from reflex.utils import console, format
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgTypeMismatch
from reflex.utils.exceptions import (
EventFnArgMismatch,
EventHandlerArgTypeMismatch,
VarAnnotationError,
)
from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
@ -1272,11 +1276,12 @@ def call_event_handler(
event_spec: The lambda that define the argument(s) to pass to the event handler.
key: The key to pass to the event handler.
Raises:
EventHandlerArgTypeMismatch: If the event handler arguments do not match the event spec.
TypeError: If the event handler arguments are invalid.
Returns:
The event spec from calling the event handler.
# noqa: DAR401 failure
"""
event_spec_args = parse_args_spec(event_spec) # type: ignore
@ -1315,8 +1320,6 @@ def call_event_handler(
)
if event_spec_return_types:
failures = []
event_callback_spec = inspect.getfullargspec(event_callback.fn)
for event_spec_index, event_spec_return_type in enumerate(
@ -1344,25 +1347,17 @@ def call_event_handler(
compare_result = typehint_issubclass(
args_types_without_vars[i], type_hints_of_provided_callback[arg]
)
except TypeError:
# TODO: In 0.7.0, remove this block and raise the exception
# raise TypeError(
# f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}." # noqa: ERA001
# ) from e
console.warn(
except TypeError as te:
raise TypeError(
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}."
)
compare_result = False
) from te
if compare_result:
continue
else:
failure = EventHandlerArgTypeMismatch(
raise EventHandlerArgTypeMismatch(
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."
)
failures.append(failure)
failed_type_check = True
break
if not failed_type_check:
if event_spec_index:
@ -1388,14 +1383,6 @@ def call_event_handler(
)
return event_callback(*event_spec_args)
if failures:
console.deprecate(
"Mismatched event handler argument types",
"\n".join([str(f) for f in failures]),
"0.6.5",
"0.7.0",
)
return event_callback(*event_spec_args) # type: ignore
@ -1420,19 +1407,15 @@ def resolve_annotation(annotations: dict[str, Any], arg_name: str):
annotations: The annotations.
arg_name: The argument name.
Raises:
VarAnnotationError: If the annotation is not found.
Returns:
The resolved annotation.
"""
annotation = annotations.get(arg_name)
if annotation is None:
console.deprecate(
feature_name="Unannotated event handler arguments",
reason="Provide type annotations for event handler arguments.",
deprecation_version="0.6.3",
removal_version="0.7.0",
)
# Allow arbitrary attribute access two levels deep until removed.
return Dict[str, dict]
raise VarAnnotationError(arg_name, annotation)
return annotation

View File

@ -9,7 +9,6 @@ from reflex.components.sonner.toast import toast as toast
from ..utils.console import warn
from . import hooks as hooks
from .assets import asset as asset
from .client_state import ClientStateVar as ClientStateVar
from .layout import layout as layout
from .misc import run_in_thread as run_in_thread
@ -62,7 +61,6 @@ class ExperimentalNamespace(SimpleNamespace):
_x = ExperimentalNamespace(
asset=asset,
client_state=ClientStateVar.create,
hooks=hooks,
layout=layout,

View File

@ -1,37 +0,0 @@
"""Helper functions for adding assets to the app."""
from typing import Optional
from reflex import assets
from reflex.utils import console
def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
"""DEPRECATED: use `rx.asset` with `shared=True` instead.
Add an asset to the app.
Place the file next to your including python file.
Copies the file to the app's external assets directory.
Example:
```python
rx.script(src=rx._x.asset("my_custom_javascript.js"))
rx.image(src=rx._x.asset("test_image.png","subfolder"))
```
Args:
relative_filename: The relative filename of the asset.
subfolder: The directory to place the asset in.
Returns:
The relative URL to the copied asset.
"""
console.deprecate(
feature_name="rx._x.asset",
reason="Use `rx.asset` with `shared=True` instead of `rx._x.asset`.",
deprecation_version="0.6.6",
removal_version="0.7.0",
)
return assets.asset(
relative_filename, shared=True, subfolder=subfolder, _stack_level=2
)

View File

@ -1772,7 +1772,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
if (
isinstance(value, dict)
and inspect.isclass(hinted_args)
and not types.is_generic_alias(hinted_args) # py3.9-py3.10
and not types.is_generic_alias(hinted_args) # py3.10
):
if issubclass(hinted_args, Model):
# Remove non-fields from the payload

View File

@ -59,6 +59,21 @@ class VarAttributeError(ReflexError, AttributeError):
"""Custom AttributeError for var related errors."""
class VarAnnotationError(ReflexError, TypeError):
"""Custom AttributeError for var annotation related errors."""
def __init__(self, var_name, annotation_value):
"""Initialize the VarAnnotationError.
Args:
var_name: The name of the var.
annotation_value: The value of the annotation.
"""
super().__init__(
f"Invalid annotation '{annotation_value}' for var '{var_name}'."
)
class UploadValueError(ReflexError, ValueError):
"""Custom ValueError for upload related errors."""

View File

@ -495,48 +495,3 @@ def is_prod_mode() -> bool:
"""
current_mode = environment.REFLEX_ENV_MODE.get()
return current_mode == constants.Env.PROD
def is_frontend_only() -> bool:
"""Check if the app is running in frontend-only mode.
Returns:
True if the app is running in frontend-only mode.
"""
console.deprecate(
"is_frontend_only() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_FRONTEND_ONLY.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_FRONTEND_ONLY.get()
def is_backend_only() -> bool:
"""Check if the app is running in backend-only mode.
Returns:
True if the app is running in backend-only mode.
"""
console.deprecate(
"is_backend_only() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_BACKEND_ONLY.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_BACKEND_ONLY.get()
def should_skip_compile() -> bool:
"""Whether the app should skip compile.
Returns:
True if the app should skip compile.
"""
console.deprecate(
"should_skip_compile() is deprecated and will be removed in a future release.",
reason="Use `environment.REFLEX_SKIP_COMPILE.get()` instead.",
deprecation_version="0.6.5",
removal_version="0.7.0",
)
return environment.REFLEX_SKIP_COMPILE.get()

View File

@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union
from reflex import constants
from reflex.constants.state import FRONTEND_EVENT_STATE
from reflex.utils import exceptions
from reflex.utils.console import deprecate
if TYPE_CHECKING:
from reflex.components.component import ComponentStyle
@ -502,37 +501,6 @@ if TYPE_CHECKING:
from reflex.vars import Var
def format_event_chain(
event_chain: EventChain | Var[EventChain],
event_arg: Var | None = None,
) -> str:
"""DEPRECATED: format an event chain as a javascript invocation.
Use str(rx.Var.create(event_chain)) instead.
Args:
event_chain: The event chain to format.
event_arg: this argument is ignored.
Returns:
Compiled javascript code to queue the given event chain on the frontend.
"""
deprecate(
feature_name="format_event_chain",
reason="Use str(rx.Var.create(event_chain)) instead",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
from reflex.vars import Var
from reflex.vars.function import ArgsFunctionOperation
result = Var.create(event_chain)
if isinstance(result, ArgsFunctionOperation):
result = result._return_expr
return str(result)
def format_queue_events(
events: EventType | None = None,
args_spec: Optional[ArgsSpec] = None,

View File

@ -75,18 +75,6 @@ def get_web_dir() -> Path:
return environment.REFLEX_WEB_WORKDIR.get()
def _python_version_check():
"""Emit deprecation warning for deprecated python versions."""
# Check for end-of-life python versions.
if sys.version_info < (3, 10):
console.deprecate(
feature_name="Support for Python 3.9 and older",
reason="please upgrade to Python 3.10 or newer",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
def check_latest_package_version(package_name: str):
"""Check if the latest version of the package is installed.
@ -109,8 +97,6 @@ def check_latest_package_version(package_name: str):
console.warn(
f"Your version ({current_version}) of {package_name} is out of date. Upgrade to {latest_version} with 'pip install {package_name} --upgrade'"
)
# Check for deprecated python versions
_python_version_check()
except Exception:
pass

View File

@ -122,7 +122,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
return {}
if UTC is None:
# for python 3.9 & 3.10
# for python 3.10
stamp = datetime.utcnow().isoformat()
else:
# for python 3.11 & 3.12

View File

@ -541,38 +541,17 @@ class Var(Generic[VAR_TYPE]):
def create(
cls,
value: Any,
_var_is_local: bool | None = None,
_var_is_string: bool | None = None,
_var_data: VarData | None = None,
) -> Var:
"""Create a var from a value.
Args:
value: The value to create the var from.
_var_is_local: Whether the var is local. Deprecated.
_var_is_string: Whether the var is a string literal. Deprecated.
_var_data: Additional hooks and imports associated with the Var.
Returns:
The var.
"""
if _var_is_local is not None:
console.deprecate(
feature_name="_var_is_local",
reason="The _var_is_local argument is not supported for Var."
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
if _var_is_string is not None:
console.deprecate(
feature_name="_var_is_string",
reason="The _var_is_string argument is not supported for Var."
"If you want to create a Var from a raw Javascript expression, use the constructor directly",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
# If the value is already a var, do nothing.
if isinstance(value, Var):
return value
@ -581,12 +560,6 @@ class Var(Generic[VAR_TYPE]):
if not isinstance(value, str):
return LiteralVar.create(value)
if _var_is_string is False or _var_is_local is True:
return cls(
_js_expr=value,
_var_data=_var_data,
)
return LiteralVar.create(value, _var_data=_var_data)
@classmethod

View File

@ -1,4 +1,4 @@
FROM python:3.9
FROM python:3.10
ARG USERNAME=kerrigan
RUN useradd -m $USERNAME

View File

@ -37,19 +37,6 @@ def test_shared_asset() -> None:
assert not Path(Path.cwd() / "assets/external").exists()
def test_deprecated_x_asset(capsys) -> None:
"""Test that the deprecated asset function raises a warning.
Args:
capsys: Pytest fixture that captures stdout and stderr.
"""
assert rx.asset("custom_script.js", shared=True) == rx._x.asset("custom_script.js")
assert (
"DeprecationWarning: rx._x.asset has been deprecated in version 0.6.6"
in capsys.readouterr().out
)
@pytest.mark.parametrize(
"path,shared",
[

View File

@ -27,7 +27,11 @@ from reflex.event import (
from reflex.state import BaseState
from reflex.style import Style
from reflex.utils import imports
from reflex.utils.exceptions import EventFnArgMismatch
from reflex.utils.exceptions import (
EventFnArgMismatch,
EventHandlerArgTypeMismatch,
VarAnnotationError,
)
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
@ -94,11 +98,14 @@ def component2() -> Type[Component]:
A test component.
"""
def on_prop_event_spec(e0: Any):
return [e0]
class TestComponent2(Component):
# A test list prop.
arr: Var[List[str]]
on_prop_event: EventHandler[lambda e0: [e0]]
on_prop_event: EventHandler[on_prop_event_spec]
def get_event_triggers(self) -> Dict[str, Any]:
"""Test controlled triggers.
@ -842,13 +849,8 @@ def test_component_event_trigger_arbitrary_args():
"on_foo": on_foo_spec,
}
comp = C1.create(on_foo=C1State.mock_handler)
assert comp.render()["props"][0] == (
"onFoo={((__e, _alpha, _bravo, _charlie) => (addEvents("
f'[(Event("{C1State.get_full_name()}.mock_handler", ({{ ["_e"] : __e["target"]["value"], ["_bravo"] : _bravo["nested"], ["_charlie"] : (_charlie["custom"] + 42) }}), ({{ }})))], '
"[__e, _alpha, _bravo, _charlie], ({ }))))}"
)
with pytest.raises(VarAnnotationError):
C1.create(on_foo=C1State.mock_handler)
def test_create_custom_component(my_component):
@ -914,21 +916,20 @@ def test_invalid_event_handler_args(component2, test_state):
on_click=[test_state.do_something_arg, test_state.do_something]
)
# Enable when 0.7.0 happens
# # Event Handler types must match
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(
# on_user_visited_count_changed=test_state.do_something_with_bool # noqa: ERA001 RUF100
# ) # noqa: ERA001 RUF100
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(on_user_list_changed=test_state.do_something_with_int) #noqa: ERA001
# with pytest.raises(EventHandlerArgTypeMismatch):
# component2.create(on_user_list_changed=test_state.do_something_with_list_int) #noqa: ERA001
with pytest.raises(EventHandlerArgTypeMismatch):
component2.create(
on_user_visited_count_changed=test_state.do_something_with_bool
)
with pytest.raises(EventHandlerArgTypeMismatch):
component2.create(on_user_list_changed=test_state.do_something_with_int)
with pytest.raises(EventHandlerArgTypeMismatch):
component2.create(on_user_list_changed=test_state.do_something_with_list_int)
# component2.create(on_open=test_state.do_something_with_int) #noqa: ERA001
# component2.create(on_open=test_state.do_something_with_bool) #noqa: ERA001
# component2.create(on_user_visited_count_changed=test_state.do_something_with_int) #noqa: ERA001
# component2.create(on_user_list_changed=test_state.do_something_with_list_str) #noqa: ERA001
component2.create(on_open=test_state.do_something_with_int)
component2.create(on_open=test_state.do_something_with_bool)
component2.create(on_user_visited_count_changed=test_state.do_something_with_int)
component2.create(on_user_list_changed=test_state.do_something_with_list_str)
# lambda cannot return weird values.
with pytest.raises(ValueError):
@ -1798,21 +1799,15 @@ def test_custom_component_declare_event_handlers_in_fields():
"""
return {
**super().get_event_triggers(),
"on_a": lambda e0: [e0],
"on_b": input_event,
"on_c": lambda e0: [],
"on_d": lambda: [],
"on_e": lambda: [],
"on_f": lambda a, b, c: [c, b, a],
}
class TestComponent(Component):
on_a: EventHandler[lambda e0: [e0]]
on_b: EventHandler[input_event]
on_c: EventHandler[no_args_event_spec]
on_d: EventHandler[no_args_event_spec]
on_e: EventHandler
on_f: EventHandler[lambda a, b, c: [c, b, a]]
custom_component = ReferenceComponent.create()
test_component = TestComponent.create()