removing deprecated features for 0.7.0 and removing py3.9 support (#4586)
* remove deprecated features and support for py3.9 * remove other deprecated stuff * update lock file * fix units tests * relock poetry * fix _replace for computed_var * fix some merge typo * fix typing of deploy args * fix benchmarks.yml versions * console.error instead of raising Exception * fix tests * ignore lambdas when resolving annotations * simplify redirect logic in event.py * more fixes * fix unit tests again * give back default annotations for lambdas * fix signature check for on_submit * remove useless stuff * update pyi * readd the getattr * raise if log_level is wrong type * silly goose, loglevel is a subclass of str * i don't believe this code * add guard --------- Co-authored-by: Khaleel Al-Adhami <khaleel.aladhami@gmail.com>
This commit is contained in:
parent
db13fe65b8
commit
4c2b2ed1c6
14
.github/workflows/benchmarks.yml
vendored
14
.github/workflows/benchmarks.yml
vendored
@ -81,24 +81,18 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# Show OS combos first in GUI
|
# Show OS combos first in GUI
|
||||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||||
python-version: ["3.9.21", "3.10.16", "3.11.11", "3.12.8"]
|
python-version: ['3.10.16', '3.11.11', '3.12.8']
|
||||||
exclude:
|
exclude:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.16"
|
python-version: '3.10.16'
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.21"
|
|
||||||
# keep only one python version for MacOS
|
# keep only one python version for MacOS
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python-version: "3.9.21"
|
python-version: '3.10.16'
|
||||||
- os: macos-latest
|
|
||||||
python-version: "3.10.16"
|
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
python-version: "3.11.11"
|
python-version: "3.11.11"
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.11"
|
python-version: '3.10.11'
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.13"
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
@ -16,7 +16,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: ./.github/actions/setup_build_env
|
- uses: ./.github/actions/setup_build_env
|
||||||
with:
|
with:
|
||||||
python-version: "3.9.21"
|
python-version: '3.10'
|
||||||
run-poetry-install: true
|
run-poetry-install: true
|
||||||
create-venv-at-path: .venv
|
create-venv-at-path: .venv
|
||||||
|
|
||||||
|
11
.github/workflows/integration_tests.yml
vendored
11
.github/workflows/integration_tests.yml
vendored
@ -43,22 +43,17 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
# Show OS combos first in GUI
|
# Show OS combos first in GUI
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
python-version: ["3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"]
|
python-version: ['3.10.16', '3.11.11', '3.12.8', '3.13.1']
|
||||||
# Windows is a bit behind on Python version availability in Github
|
|
||||||
exclude:
|
exclude:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.11.11"
|
python-version: "3.11.11"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.16"
|
python-version: '3.10.16'
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.21"
|
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.11.9"
|
python-version: "3.11.9"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.11"
|
python-version: '3.10.11'
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.13"
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
|
10
.github/workflows/unit_tests.yml
vendored
10
.github/workflows/unit_tests.yml
vendored
@ -28,22 +28,18 @@ jobs:
|
|||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-latest, windows-latest]
|
os: [ubuntu-latest, windows-latest]
|
||||||
python-version: ["3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"]
|
python-version: ["3.10.16", "3.11.11", "3.12.8", "3.13.1"]
|
||||||
# Windows is a bit behind on Python version availability in Github
|
# Windows is a bit behind on Python version availability in Github
|
||||||
exclude:
|
exclude:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.11.11"
|
python-version: "3.11.11"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.16"
|
python-version: "3.10.16"
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.21"
|
|
||||||
include:
|
include:
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.11.9"
|
python-version: "3.11.9"
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
python-version: "3.10.11"
|
python-version: "3.10.11"
|
||||||
- os: windows-latest
|
|
||||||
python-version: "3.9.13"
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
# Service containers to run with `runner-job`
|
# Service containers to run with `runner-job`
|
||||||
@ -92,8 +88,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
# Note: py39, py310, py311 versions chosen due to available arm64 darwin builds.
|
# Note: py310, py311 versions chosen due to available arm64 darwin builds.
|
||||||
python-version: ["3.9.13", "3.10.11", "3.11.9", "3.12.8", "3.13.1"]
|
python-version: ["3.10.11", "3.11.9", "3.12.8", "3.13.1"]
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
@ -28,7 +28,7 @@ repos:
|
|||||||
entry: python3 scripts/make_pyi.py
|
entry: python3 scripts/make_pyi.py
|
||||||
|
|
||||||
- repo: https://github.com/RobertCraigie/pyright-python
|
- repo: https://github.com/RobertCraigie/pyright-python
|
||||||
rev: v1.1.313
|
rev: v1.1.334
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyright
|
- id: pyright
|
||||||
args: [reflex, tests]
|
args: [reflex, tests]
|
||||||
|
@ -8,7 +8,7 @@ Here is a quick guide on how to run Reflex repo locally so you can start contrib
|
|||||||
|
|
||||||
**Prerequisites:**
|
**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).
|
- 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:**
|
**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.
|
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
|
``` bash
|
||||||
pre-commit install
|
pre-commit install
|
||||||
|
@ -34,7 +34,7 @@ See our [architecture page](https://reflex.dev/blog/2024-03-21-reflex-architectu
|
|||||||
|
|
||||||
## ⚙️ Installation
|
## ⚙️ Installation
|
||||||
|
|
||||||
Open a terminal and run (Requires Python 3.9+):
|
Open a terminal and run (Requires Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -34,7 +34,7 @@ Auf unserer [Architektur-Seite](https://reflex.dev/blog/2024-03-21-reflex-archit
|
|||||||
|
|
||||||
## ⚙️ Installation
|
## ⚙️ 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
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -35,7 +35,7 @@ Consulta nuestra [página de arquitectura](https://reflex.dev/blog/2024-03-21-re
|
|||||||
|
|
||||||
## ⚙️ Instalación
|
## ⚙️ Instalación
|
||||||
|
|
||||||
Abra un terminal y ejecute (Requiere Python 3.9+):
|
Abra un terminal y ejecute (Requiere Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -35,7 +35,7 @@ Reflex के अंदर के कामकाज को जानने क
|
|||||||
|
|
||||||
## ⚙️ इंस्टॉलेशन (Installation)
|
## ⚙️ इंस्टॉलेशन (Installation)
|
||||||
|
|
||||||
एक टर्मिनल खोलें और चलाएं (Python 3.9+ की आवश्यकता है):
|
एक टर्मिनल खोलें और चलाएं (Python 3.10+ की आवश्यकता है):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
## ⚙️ Installazione
|
## ⚙️ Installazione
|
||||||
|
|
||||||
Apri un terminale ed esegui (Richiede Python 3.9+):
|
Apri un terminale ed esegui (Richiede Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -37,7 +37,7 @@ Reflex がどのように動作しているかを知るには、[アーキテク
|
|||||||
|
|
||||||
## ⚙️ インストール
|
## ⚙️ インストール
|
||||||
|
|
||||||
ターミナルを開いて以下のコマンドを実行してください。(Python 3.9 以上が必要です。):
|
ターミナルを開いて以下のコマンドを実行してください。(Python 3.10 以上が必要です。):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
---
|
---
|
||||||
## ⚙️ 설치
|
## ⚙️ 설치
|
||||||
|
|
||||||
터미널을 열고 실행하세요. (Python 3.9+ 필요):
|
터미널을 열고 실행하세요. (Python 3.10+ 필요):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -34,7 +34,7 @@
|
|||||||
|
|
||||||
## ⚙️ Installation - نصب و راه اندازی
|
## ⚙️ Installation - نصب و راه اندازی
|
||||||
|
|
||||||
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.9+):
|
یک ترمینال را باز کنید و اجرا کنید (نیازمند Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
---
|
---
|
||||||
## ⚙️ Instalação
|
## ⚙️ Instalação
|
||||||
|
|
||||||
Abra um terminal e execute (Requer Python 3.9+):
|
Abra um terminal e execute (Requer Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
## ⚙️ Kurulum
|
## ⚙️ 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
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -34,7 +34,7 @@ Các tính năng chính:
|
|||||||
|
|
||||||
## ⚙️ Cài đặt
|
## ⚙️ 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
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -34,7 +34,7 @@ Reflex 是一个使用纯Python构建全栈web应用的库。
|
|||||||
|
|
||||||
## ⚙️ 安装
|
## ⚙️ 安装
|
||||||
|
|
||||||
打开一个终端并且运行(要求Python3.9+):
|
打开一个终端并且运行(要求Python3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
@ -36,7 +36,7 @@ Reflex 是一個可以用純 Python 構建全端網頁應用程式的函式庫
|
|||||||
|
|
||||||
## ⚙️ 安裝
|
## ⚙️ 安裝
|
||||||
|
|
||||||
開啟一個終端機並且執行 (需要 Python 3.9+):
|
開啟一個終端機並且執行 (需要 Python 3.10+):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
pip install reflex
|
pip install reflex
|
||||||
|
1437
poetry.lock
generated
1437
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ keywords = ["web", "framework"]
|
|||||||
classifiers = ["Development Status :: 4 - Beta"]
|
classifiers = ["Development Status :: 4 - Beta"]
|
||||||
|
|
||||||
[tool.poetry.dependencies]
|
[tool.poetry.dependencies]
|
||||||
python = "^3.9"
|
python = "^3.10"
|
||||||
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
|
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
|
||||||
gunicorn = ">=20.1.0,<24.0"
|
gunicorn = ">=20.1.0,<24.0"
|
||||||
jinja2 = ">=3.1.2,<4.0"
|
jinja2 = ">=3.1.2,<4.0"
|
||||||
@ -50,7 +50,6 @@ httpx = ">=0.25.1,<1.0"
|
|||||||
twine = ">=4.0.0,<7.0"
|
twine = ">=4.0.0,<7.0"
|
||||||
tomlkit = ">=0.12.4,<1.0"
|
tomlkit = ">=0.12.4,<1.0"
|
||||||
lazy_loader = ">=0.4"
|
lazy_loader = ">=0.4"
|
||||||
reflex-chakra = ">=0.6.0"
|
|
||||||
typing_extensions = ">=4.6.0"
|
typing_extensions = ">=4.6.0"
|
||||||
|
|
||||||
[tool.poetry.group.dev.dependencies]
|
[tool.poetry.group.dev.dependencies]
|
||||||
@ -102,5 +101,5 @@ asyncio_default_fixture_loop_scope = "function"
|
|||||||
asyncio_mode = "auto"
|
asyncio_mode = "auto"
|
||||||
|
|
||||||
[tool.codespell]
|
[tool.codespell]
|
||||||
skip = "docs/*,*.html,examples/*, *.pyi"
|
skip = "docs/*,*.html,examples/*, *.pyi, poetry.lock"
|
||||||
ignore-words-list = "te, TreeE"
|
ignore-words-list = "te, TreeE"
|
||||||
|
@ -8,7 +8,7 @@ version = "0.0.1"
|
|||||||
description = "Reflex custom component {{ module_name }}"
|
description = "Reflex custom component {{ module_name }}"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
license = { text = "Apache-2.0" }
|
license = { text = "Apache-2.0" }
|
||||||
requires-python = ">=3.9"
|
requires-python = ">=3.10"
|
||||||
authors = [{ name = "", email = "YOUREMAIL@domain.com" }]
|
authors = [{ name = "", email = "YOUREMAIL@domain.com" }]
|
||||||
keywords = ["reflex","reflex-custom-components"]
|
keywords = ["reflex","reflex-custom-components"]
|
||||||
|
|
||||||
|
@ -303,7 +303,6 @@ _MAPPING: dict = {
|
|||||||
"event": [
|
"event": [
|
||||||
"EventChain",
|
"EventChain",
|
||||||
"EventHandler",
|
"EventHandler",
|
||||||
"background",
|
|
||||||
"call_script",
|
"call_script",
|
||||||
"call_function",
|
"call_function",
|
||||||
"run_script",
|
"run_script",
|
||||||
@ -367,19 +366,4 @@ getattr, __dir__, __all__ = lazy_loader.attach(
|
|||||||
|
|
||||||
|
|
||||||
def __getattr__(name):
|
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)
|
return getattr(name)
|
||||||
|
@ -156,7 +156,6 @@ from .constants import Env as Env
|
|||||||
from .constants.colors import Color as Color
|
from .constants.colors import Color as Color
|
||||||
from .event import EventChain as EventChain
|
from .event import EventChain as EventChain
|
||||||
from .event import EventHandler as EventHandler
|
from .event import EventHandler as EventHandler
|
||||||
from .event import background as background
|
|
||||||
from .event import call_function as call_function
|
from .event import call_function as call_function
|
||||||
from .event import call_script as call_script
|
from .event import call_script as call_script
|
||||||
from .event import clear_local_storage as clear_local_storage
|
from .event import clear_local_storage as clear_local_storage
|
||||||
|
@ -23,8 +23,6 @@ from typing import (
|
|||||||
Union,
|
Union,
|
||||||
)
|
)
|
||||||
|
|
||||||
from typing_extensions import deprecated
|
|
||||||
|
|
||||||
import reflex.state
|
import reflex.state
|
||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.compiler.templates import STATEFUL_COMPONENT
|
from reflex.compiler.templates import STATEFUL_COMPONENT
|
||||||
@ -47,11 +45,10 @@ from reflex.event import (
|
|||||||
EventChain,
|
EventChain,
|
||||||
EventHandler,
|
EventHandler,
|
||||||
EventSpec,
|
EventSpec,
|
||||||
EventVar,
|
|
||||||
no_args_event_spec,
|
no_args_event_spec,
|
||||||
)
|
)
|
||||||
from reflex.style import Style, format_as_emotion
|
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 (
|
from reflex.utils.imports import (
|
||||||
ImmutableParsedImportDict,
|
ImmutableParsedImportDict,
|
||||||
ImportDict,
|
ImportDict,
|
||||||
@ -547,41 +544,6 @@ class Component(BaseComponent, ABC):
|
|||||||
# Construct the component.
|
# Construct the component.
|
||||||
super().__init__(*args, **kwargs)
|
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(
|
def get_event_triggers(
|
||||||
self,
|
self,
|
||||||
) -> Dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]:
|
) -> Dict[str, types.ArgsSpec | Sequence[types.ArgsSpec]]:
|
||||||
|
@ -14,7 +14,7 @@ from reflex.components.radix.themes.layout.box import Box
|
|||||||
from reflex.constants.colors import Color
|
from reflex.constants.colors import Color
|
||||||
from reflex.event import set_clipboard
|
from reflex.event import set_clipboard
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils import console, format
|
from reflex.utils import format
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
from reflex.vars.base import LiteralVar, Var, VarData
|
from reflex.vars.base import LiteralVar, Var, VarData
|
||||||
|
|
||||||
@ -438,6 +438,8 @@ class CodeBlock(Component, MarkdownComponentMap):
|
|||||||
can_copy = props.pop("can_copy", False)
|
can_copy = props.pop("can_copy", False)
|
||||||
copy_button = props.pop("copy_button", None)
|
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:
|
if "theme" not in props:
|
||||||
# Default color scheme responds to global color mode.
|
# Default color scheme responds to global color mode.
|
||||||
props["theme"] = color_mode_cond(
|
props["theme"] = color_mode_cond(
|
||||||
@ -445,17 +447,6 @@ class CodeBlock(Component, MarkdownComponentMap):
|
|||||||
dark=Theme.one_dark,
|
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:
|
if can_copy:
|
||||||
code = children[0]
|
code = children[0]
|
||||||
copy_button = ( # type: ignore
|
copy_button = ( # type: ignore
|
||||||
|
@ -102,7 +102,7 @@ class Fieldset(Element):
|
|||||||
name: Var[Union[str, int, bool]]
|
name: Var[Union[str, int, bool]]
|
||||||
|
|
||||||
|
|
||||||
def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
|
def on_submit_event_spec() -> Tuple[Var[dict[str, Any]]]:
|
||||||
"""Event handler spec for the on_submit event.
|
"""Event handler spec for the on_submit event.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -111,7 +111,7 @@ def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
|
|||||||
return (FORM_DATA,)
|
return (FORM_DATA,)
|
||||||
|
|
||||||
|
|
||||||
def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]:
|
def on_submit_string_event_spec() -> Tuple[Var[dict[str, str]]]:
|
||||||
"""Event handler spec for the on_submit event.
|
"""Event handler spec for the on_submit event.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
@ -270,8 +270,8 @@ class Fieldset(Element):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: ...
|
def on_submit_event_spec() -> Tuple[Var[dict[str, Any]]]: ...
|
||||||
def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]: ...
|
def on_submit_string_event_spec() -> Tuple[Var[dict[str, str]]]: ...
|
||||||
|
|
||||||
class Form(BaseHTML):
|
class Form(BaseHTML):
|
||||||
@overload
|
@overload
|
||||||
@ -341,10 +341,10 @@ class Form(BaseHTML):
|
|||||||
on_submit: Optional[
|
on_submit: Optional[
|
||||||
Union[
|
Union[
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
|
||||||
],
|
],
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
|
@ -132,10 +132,10 @@ class FormRoot(FormComponent, HTMLForm):
|
|||||||
on_submit: Optional[
|
on_submit: Optional[
|
||||||
Union[
|
Union[
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
|
||||||
],
|
],
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
@ -608,10 +608,10 @@ class Form(FormRoot):
|
|||||||
on_submit: Optional[
|
on_submit: Optional[
|
||||||
Union[
|
Union[
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
|
||||||
],
|
],
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
@ -741,10 +741,10 @@ class FormNamespace(ComponentNamespace):
|
|||||||
on_submit: Optional[
|
on_submit: Optional[
|
||||||
Union[
|
Union[
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
|
||||||
],
|
],
|
||||||
Union[
|
Union[
|
||||||
EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
|
EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
] = None,
|
] = None,
|
||||||
|
176
reflex/event.py
176
reflex/event.py
@ -25,7 +25,6 @@ from typing import (
|
|||||||
overload,
|
overload,
|
||||||
)
|
)
|
||||||
|
|
||||||
import typing_extensions
|
|
||||||
from typing_extensions import (
|
from typing_extensions import (
|
||||||
Concatenate,
|
Concatenate,
|
||||||
ParamSpec,
|
ParamSpec,
|
||||||
@ -40,7 +39,11 @@ from typing_extensions import (
|
|||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.constants.state import FRONTEND_EVENT_STATE
|
from reflex.constants.state import FRONTEND_EVENT_STATE
|
||||||
from reflex.utils import console, format
|
from reflex.utils import console, format
|
||||||
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgTypeMismatch
|
from reflex.utils.exceptions import (
|
||||||
|
EventFnArgMismatchError,
|
||||||
|
EventHandlerArgTypeMismatchError,
|
||||||
|
MissingAnnotationError,
|
||||||
|
)
|
||||||
from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
|
from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import LiteralVar, Var
|
from reflex.vars.base import LiteralVar, Var
|
||||||
@ -96,32 +99,6 @@ _EVENT_FIELDS: set[str] = {f.name for f in dataclasses.fields(Event)}
|
|||||||
BACKGROUND_TASK_MARKER = "_reflex_background_task"
|
BACKGROUND_TASK_MARKER = "_reflex_background_task"
|
||||||
|
|
||||||
|
|
||||||
def background(fn, *, __internal_reflex_call: bool = False):
|
|
||||||
"""Decorator to mark event handler as running in the background.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
fn: The function to decorate.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The same function, but with a marker set.
|
|
||||||
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
TypeError: If the function is not a coroutine function or async generator.
|
|
||||||
"""
|
|
||||||
if not __internal_reflex_call:
|
|
||||||
console.deprecate(
|
|
||||||
"background-decorator",
|
|
||||||
"Use `rx.event(background=True)` instead.",
|
|
||||||
"0.6.5",
|
|
||||||
"0.7.0",
|
|
||||||
)
|
|
||||||
if not inspect.iscoroutinefunction(fn) and not inspect.isasyncgenfunction(fn):
|
|
||||||
raise TypeError("Background task must be async function or generator.")
|
|
||||||
setattr(fn, BACKGROUND_TASK_MARKER, True)
|
|
||||||
return fn
|
|
||||||
|
|
||||||
|
|
||||||
@dataclasses.dataclass(
|
@dataclasses.dataclass(
|
||||||
init=True,
|
init=True,
|
||||||
frozen=True,
|
frozen=True,
|
||||||
@ -813,29 +790,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def redirect(
|
def redirect(
|
||||||
path: str | Var[str],
|
path: str | Var[str],
|
||||||
is_external: Optional[bool] = None,
|
is_external: bool = False,
|
||||||
replace: bool = False,
|
replace: bool = False,
|
||||||
) -> EventSpec: ...
|
|
||||||
|
|
||||||
|
|
||||||
@overload
|
|
||||||
@typing_extensions.deprecated("`external` is deprecated use `is_external` instead")
|
|
||||||
def redirect(
|
|
||||||
path: str | Var[str],
|
|
||||||
is_external: Optional[bool] = None,
|
|
||||||
replace: bool = False,
|
|
||||||
external: Optional[bool] = None,
|
|
||||||
) -> EventSpec: ...
|
|
||||||
|
|
||||||
|
|
||||||
def redirect(
|
|
||||||
path: str | Var[str],
|
|
||||||
is_external: Optional[bool] = None,
|
|
||||||
replace: bool = False,
|
|
||||||
external: Optional[bool] = None,
|
|
||||||
) -> EventSpec:
|
) -> EventSpec:
|
||||||
"""Redirect to a new path.
|
"""Redirect to a new path.
|
||||||
|
|
||||||
@ -843,26 +801,10 @@ def redirect(
|
|||||||
path: The path to redirect to.
|
path: The path to redirect to.
|
||||||
is_external: Whether to open in new tab or not.
|
is_external: Whether to open in new tab or not.
|
||||||
replace: If True, the current page will not create a new history entry.
|
replace: If True, the current page will not create a new history entry.
|
||||||
external(Deprecated): Whether to open in new tab or not.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
An event to redirect to the path.
|
An event to redirect to the path.
|
||||||
"""
|
"""
|
||||||
if external is not None:
|
|
||||||
console.deprecate(
|
|
||||||
"The `external` prop in `rx.redirect`",
|
|
||||||
"use `is_external` instead.",
|
|
||||||
"0.6.6",
|
|
||||||
"0.7.0",
|
|
||||||
)
|
|
||||||
|
|
||||||
# is_external should take precedence over external.
|
|
||||||
is_external = (
|
|
||||||
(False if external is None else external)
|
|
||||||
if is_external is None
|
|
||||||
else is_external
|
|
||||||
)
|
|
||||||
|
|
||||||
return server_side(
|
return server_side(
|
||||||
"_redirect",
|
"_redirect",
|
||||||
get_fn_signature(redirect),
|
get_fn_signature(redirect),
|
||||||
@ -1279,11 +1221,14 @@ def call_event_handler(
|
|||||||
event_spec: The lambda that define the argument(s) to pass to the 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.
|
key: The key to pass to the event handler.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
EventHandlerArgTypeMismatchError: If the event handler arguments do not match the event spec. #noqa: DAR402
|
||||||
|
TypeError: If the event handler arguments are invalid.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The event spec from calling the event handler.
|
The event spec from calling the event handler.
|
||||||
|
|
||||||
# noqa: DAR401 failure
|
#noqa: DAR401
|
||||||
|
|
||||||
"""
|
"""
|
||||||
event_spec_args = parse_args_spec(event_spec) # type: ignore
|
event_spec_args = parse_args_spec(event_spec) # type: ignore
|
||||||
|
|
||||||
@ -1320,10 +1265,15 @@ def call_event_handler(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
type_match_found: dict[str, bool] = {}
|
||||||
|
delayed_exceptions: list[EventHandlerArgTypeMismatchError] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
||||||
|
except NameError:
|
||||||
|
type_hints_of_provided_callback = {}
|
||||||
|
|
||||||
if event_spec_return_types:
|
if event_spec_return_types:
|
||||||
failures = []
|
|
||||||
|
|
||||||
event_callback_spec = inspect.getfullargspec(event_callback.fn)
|
event_callback_spec = inspect.getfullargspec(event_callback.fn)
|
||||||
|
|
||||||
for event_spec_index, event_spec_return_type in enumerate(
|
for event_spec_index, event_spec_return_type in enumerate(
|
||||||
@ -1335,43 +1285,35 @@ def call_event_handler(
|
|||||||
arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
|
arg if get_origin(arg) is not Var else get_args(arg)[0] for arg in args
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
|
||||||
type_hints_of_provided_callback = get_type_hints(event_callback.fn)
|
|
||||||
except NameError:
|
|
||||||
type_hints_of_provided_callback = {}
|
|
||||||
|
|
||||||
failed_type_check = False
|
|
||||||
|
|
||||||
# check that args of event handler are matching the spec if type hints are provided
|
# check that args of event handler are matching the spec if type hints are provided
|
||||||
for i, arg in enumerate(event_callback_spec.args[1:]):
|
for i, arg in enumerate(event_callback_spec.args[1:]):
|
||||||
if arg not in type_hints_of_provided_callback:
|
if arg not in type_hints_of_provided_callback:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
type_match_found.setdefault(arg, False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
compare_result = typehint_issubclass(
|
compare_result = typehint_issubclass(
|
||||||
args_types_without_vars[i], type_hints_of_provided_callback[arg]
|
args_types_without_vars[i], type_hints_of_provided_callback[arg]
|
||||||
)
|
)
|
||||||
except TypeError:
|
except TypeError as te:
|
||||||
# TODO: In 0.7.0, remove this block and raise the exception
|
raise TypeError(
|
||||||
# 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(
|
|
||||||
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}."
|
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}."
|
||||||
)
|
) from te
|
||||||
compare_result = False
|
|
||||||
|
|
||||||
if compare_result:
|
if compare_result:
|
||||||
|
type_match_found[arg] = True
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
failure = EventHandlerArgTypeMismatch(
|
type_match_found[arg] = False
|
||||||
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."
|
delayed_exceptions.append(
|
||||||
|
EventHandlerArgTypeMismatchError(
|
||||||
|
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 all(type_match_found.values()):
|
||||||
|
delayed_exceptions.clear()
|
||||||
if event_spec_index:
|
if event_spec_index:
|
||||||
args = get_args(event_spec_return_types[0])
|
args = get_args(event_spec_return_types[0])
|
||||||
|
|
||||||
@ -1393,15 +1335,10 @@ def call_event_handler(
|
|||||||
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
|
f"Event handler {key} expects ({expect_string}) -> () but got ({given_string}) -> () as annotated in {event_callback.fn.__qualname__} instead. "
|
||||||
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
|
f"This may lead to unexpected behavior but is intentionally ignored for {key}."
|
||||||
)
|
)
|
||||||
return event_callback(*event_spec_args)
|
break
|
||||||
|
|
||||||
if failures:
|
if delayed_exceptions:
|
||||||
console.deprecate(
|
raise delayed_exceptions[0]
|
||||||
"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
|
return event_callback(*event_spec_args) # type: ignore
|
||||||
|
|
||||||
@ -1420,26 +1357,26 @@ def unwrap_var_annotation(annotation: GenericType):
|
|||||||
return annotation
|
return annotation
|
||||||
|
|
||||||
|
|
||||||
def resolve_annotation(annotations: dict[str, Any], arg_name: str):
|
def resolve_annotation(annotations: dict[str, Any], arg_name: str, spec: ArgsSpec):
|
||||||
"""Resolve the annotation for the given argument name.
|
"""Resolve the annotation for the given argument name.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
annotations: The annotations.
|
annotations: The annotations.
|
||||||
arg_name: The argument name.
|
arg_name: The argument name.
|
||||||
|
spec: The specs which the annotations come from.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
MissingAnnotationError: If the annotation is missing for non-lambda methods.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The resolved annotation.
|
The resolved annotation.
|
||||||
"""
|
"""
|
||||||
annotation = annotations.get(arg_name)
|
annotation = annotations.get(arg_name)
|
||||||
if annotation is None:
|
if annotation is None:
|
||||||
console.deprecate(
|
if not isinstance(spec, types.LambdaType):
|
||||||
feature_name="Unannotated event handler arguments",
|
raise MissingAnnotationError(var_name=arg_name)
|
||||||
reason="Provide type annotations for event handler arguments.",
|
else:
|
||||||
deprecation_version="0.6.3",
|
return dict[str, dict]
|
||||||
removal_version="0.7.0",
|
|
||||||
)
|
|
||||||
# Allow arbitrary attribute access two levels deep until removed.
|
|
||||||
return Dict[str, dict]
|
|
||||||
return annotation
|
return annotation
|
||||||
|
|
||||||
|
|
||||||
@ -1461,7 +1398,13 @@ def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
|
|||||||
arg_spec(
|
arg_spec(
|
||||||
*[
|
*[
|
||||||
Var(f"_{l_arg}").to(
|
Var(f"_{l_arg}").to(
|
||||||
unwrap_var_annotation(resolve_annotation(annotations, l_arg))
|
unwrap_var_annotation(
|
||||||
|
resolve_annotation(
|
||||||
|
annotations,
|
||||||
|
l_arg,
|
||||||
|
spec=arg_spec,
|
||||||
|
)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
for l_arg in spec.args
|
for l_arg in spec.args
|
||||||
]
|
]
|
||||||
@ -1477,7 +1420,7 @@ def check_fn_match_arg_spec(
|
|||||||
func_name: str | None = None,
|
func_name: str | None = None,
|
||||||
):
|
):
|
||||||
"""Ensures that the function signature matches the passed argument specification
|
"""Ensures that the function signature matches the passed argument specification
|
||||||
or raises an EventFnArgMismatch if they do not.
|
or raises an EventFnArgMismatchError if they do not.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_func: The function to be validated.
|
user_func: The function to be validated.
|
||||||
@ -1487,7 +1430,7 @@ def check_fn_match_arg_spec(
|
|||||||
func_name: The name of the function to be validated.
|
func_name: The name of the function to be validated.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
EventFnArgMismatch: Raised if the number of mandatory arguments do not match
|
EventFnArgMismatchError: Raised if the number of mandatory arguments do not match
|
||||||
"""
|
"""
|
||||||
user_args = inspect.getfullargspec(user_func).args
|
user_args = inspect.getfullargspec(user_func).args
|
||||||
# Drop the first argument if it's a bound method
|
# Drop the first argument if it's a bound method
|
||||||
@ -1503,7 +1446,7 @@ def check_fn_match_arg_spec(
|
|||||||
number_of_event_args = len(parsed_event_args)
|
number_of_event_args = len(parsed_event_args)
|
||||||
|
|
||||||
if number_of_user_args - number_of_user_default_args > number_of_event_args:
|
if number_of_user_args - number_of_user_default_args > number_of_event_args:
|
||||||
raise EventFnArgMismatch(
|
raise EventFnArgMismatchError(
|
||||||
f"Event {key} only provides {number_of_event_args} arguments, but "
|
f"Event {key} only provides {number_of_event_args} arguments, but "
|
||||||
f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
|
f"{func_name or user_func} requires at least {number_of_user_args - number_of_user_default_args} "
|
||||||
"arguments to be passed to the event handler.\n"
|
"arguments to be passed to the event handler.\n"
|
||||||
@ -1812,8 +1755,6 @@ V3 = TypeVar("V3")
|
|||||||
V4 = TypeVar("V4")
|
V4 = TypeVar("V4")
|
||||||
V5 = TypeVar("V5")
|
V5 = TypeVar("V5")
|
||||||
|
|
||||||
background_event_decorator = background
|
|
||||||
|
|
||||||
|
|
||||||
class EventCallback(Generic[P, T]):
|
class EventCallback(Generic[P, T]):
|
||||||
"""A descriptor that wraps a function to be used as an event."""
|
"""A descriptor that wraps a function to be used as an event."""
|
||||||
@ -1986,6 +1927,9 @@ class EventNamespace(types.SimpleNamespace):
|
|||||||
func: The function to wrap.
|
func: The function to wrap.
|
||||||
background: Whether the event should be run in the background. Defaults to False.
|
background: Whether the event should be run in the background. Defaults to False.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
TypeError: If background is True and the function is not a coroutine or async generator. # noqa: DAR402
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The wrapped function.
|
The wrapped function.
|
||||||
"""
|
"""
|
||||||
@ -1994,7 +1938,13 @@ class EventNamespace(types.SimpleNamespace):
|
|||||||
func: Callable[Concatenate[BASE_STATE, P], T],
|
func: Callable[Concatenate[BASE_STATE, P], T],
|
||||||
) -> EventCallback[P, T]:
|
) -> EventCallback[P, T]:
|
||||||
if background is True:
|
if background is True:
|
||||||
return background_event_decorator(func, __internal_reflex_call=True) # type: ignore
|
if not inspect.iscoroutinefunction(
|
||||||
|
func
|
||||||
|
) and not inspect.isasyncgenfunction(func):
|
||||||
|
raise TypeError(
|
||||||
|
"Background task must be async function or generator."
|
||||||
|
)
|
||||||
|
setattr(func, BACKGROUND_TASK_MARKER, True)
|
||||||
return func # type: ignore
|
return func # type: ignore
|
||||||
|
|
||||||
if func is not None:
|
if func is not None:
|
||||||
|
@ -9,7 +9,6 @@ from reflex.components.sonner.toast import toast as toast
|
|||||||
|
|
||||||
from ..utils.console import warn
|
from ..utils.console import warn
|
||||||
from . import hooks as hooks
|
from . import hooks as hooks
|
||||||
from .assets import asset as asset
|
|
||||||
from .client_state import ClientStateVar as ClientStateVar
|
from .client_state import ClientStateVar as ClientStateVar
|
||||||
from .layout import layout as layout
|
from .layout import layout as layout
|
||||||
from .misc import run_in_thread as run_in_thread
|
from .misc import run_in_thread as run_in_thread
|
||||||
@ -62,7 +61,6 @@ class ExperimentalNamespace(SimpleNamespace):
|
|||||||
|
|
||||||
|
|
||||||
_x = ExperimentalNamespace(
|
_x = ExperimentalNamespace(
|
||||||
asset=asset,
|
|
||||||
client_state=ClientStateVar.create,
|
client_state=ClientStateVar.create,
|
||||||
hooks=hooks,
|
hooks=hooks,
|
||||||
layout=layout,
|
layout=layout,
|
||||||
|
@ -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
|
|
||||||
)
|
|
@ -505,6 +505,7 @@ def deploy(
|
|||||||
),
|
),
|
||||||
):
|
):
|
||||||
"""Deploy the app to the Reflex hosting service."""
|
"""Deploy the app to the Reflex hosting service."""
|
||||||
|
from reflex_cli.constants.base import LogLevel as HostingLogLevel
|
||||||
from reflex_cli.utils import dependency
|
from reflex_cli.utils import dependency
|
||||||
from reflex_cli.v2 import cli as hosting_cli
|
from reflex_cli.v2 import cli as hosting_cli
|
||||||
|
|
||||||
@ -516,6 +517,21 @@ def deploy(
|
|||||||
# Set the log level.
|
# Set the log level.
|
||||||
console.set_log_level(loglevel)
|
console.set_log_level(loglevel)
|
||||||
|
|
||||||
|
def convert_reflex_loglevel_to_reflex_cli_loglevel(
|
||||||
|
loglevel: constants.LogLevel,
|
||||||
|
) -> HostingLogLevel:
|
||||||
|
if loglevel == constants.LogLevel.DEBUG:
|
||||||
|
return HostingLogLevel.DEBUG
|
||||||
|
if loglevel == constants.LogLevel.INFO:
|
||||||
|
return HostingLogLevel.INFO
|
||||||
|
if loglevel == constants.LogLevel.WARNING:
|
||||||
|
return HostingLogLevel.WARNING
|
||||||
|
if loglevel == constants.LogLevel.ERROR:
|
||||||
|
return HostingLogLevel.ERROR
|
||||||
|
if loglevel == constants.LogLevel.CRITICAL:
|
||||||
|
return HostingLogLevel.CRITICAL
|
||||||
|
return HostingLogLevel.INFO
|
||||||
|
|
||||||
# Only check requirements if interactive.
|
# Only check requirements if interactive.
|
||||||
# There is user interaction for requirements update.
|
# There is user interaction for requirements update.
|
||||||
if interactive:
|
if interactive:
|
||||||
@ -525,9 +541,7 @@ def deploy(
|
|||||||
if prerequisites.needs_reinit(frontend=True):
|
if prerequisites.needs_reinit(frontend=True):
|
||||||
_init(name=config.app_name, loglevel=loglevel)
|
_init(name=config.app_name, loglevel=loglevel)
|
||||||
prerequisites.check_latest_package_version(constants.ReflexHostingCLI.MODULE_NAME)
|
prerequisites.check_latest_package_version(constants.ReflexHostingCLI.MODULE_NAME)
|
||||||
extra: dict[str, str] = (
|
|
||||||
{"config_path": config_path} if config_path is not None else {}
|
|
||||||
)
|
|
||||||
hosting_cli.deploy(
|
hosting_cli.deploy(
|
||||||
app_name=app_name,
|
app_name=app_name,
|
||||||
app_id=app_id,
|
app_id=app_id,
|
||||||
@ -551,12 +565,11 @@ def deploy(
|
|||||||
envfile=envfile,
|
envfile=envfile,
|
||||||
hostname=hostname,
|
hostname=hostname,
|
||||||
interactive=interactive,
|
interactive=interactive,
|
||||||
loglevel=type(loglevel).INFO, # type: ignore
|
loglevel=convert_reflex_loglevel_to_reflex_cli_loglevel(loglevel),
|
||||||
token=token,
|
token=token,
|
||||||
project=project,
|
project=project,
|
||||||
config_path=config_path,
|
|
||||||
project_name=project_name,
|
project_name=project_name,
|
||||||
**extra,
|
**({"config_path": config_path} if config_path is not None else {}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1341,12 +1341,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
if field.allow_none and not is_optional(field_type):
|
if field.allow_none and not is_optional(field_type):
|
||||||
field_type = Union[field_type, None]
|
field_type = Union[field_type, None]
|
||||||
if not _isinstance(value, field_type):
|
if not _isinstance(value, field_type):
|
||||||
console.deprecate(
|
console.error(
|
||||||
"mismatched-type-assignment",
|
f"Expected field '{type(self).__name__}.{name}' to receive type '{field_type}',"
|
||||||
f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
|
f" but got '{value}' of type '{type(value)}'."
|
||||||
" This might lead to unexpected behavior.",
|
|
||||||
"0.6.5",
|
|
||||||
"0.7.0",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Set the attribute.
|
# Set the attribute.
|
||||||
@ -1831,7 +1828,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
|||||||
if (
|
if (
|
||||||
isinstance(value, dict)
|
isinstance(value, dict)
|
||||||
and inspect.isclass(hinted_args)
|
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):
|
if issubclass(hinted_args, Model):
|
||||||
# Remove non-fields from the payload
|
# Remove non-fields from the payload
|
||||||
|
@ -287,7 +287,9 @@ class Style(dict):
|
|||||||
_var = LiteralVar.create(value)
|
_var = LiteralVar.create(value)
|
||||||
if _var is not None:
|
if _var is not None:
|
||||||
# Carry the imports/hooks when setting a Var as a value.
|
# Carry the imports/hooks when setting a Var as a value.
|
||||||
self._var_data = VarData.merge(self._var_data, _var._get_all_var_data())
|
self._var_data = VarData.merge(
|
||||||
|
getattr(self, "_var_data", None), _var._get_all_var_data()
|
||||||
|
)
|
||||||
super().__setitem__(key, value)
|
super().__setitem__(key, value)
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,20 +51,12 @@ def set_log_level(log_level: LogLevel):
|
|||||||
log_level: The log level to set.
|
log_level: The log level to set.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValueError: If the log level is invalid.
|
TypeError: If the log level is a string.
|
||||||
"""
|
"""
|
||||||
if not isinstance(log_level, LogLevel):
|
if not isinstance(log_level, LogLevel):
|
||||||
deprecate(
|
raise TypeError(
|
||||||
feature_name="Passing a string to set_log_level",
|
f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
|
||||||
reason="use reflex.constants.LogLevel enum instead",
|
|
||||||
deprecation_version="0.6.6",
|
|
||||||
removal_version="0.7.0",
|
|
||||||
)
|
)
|
||||||
try:
|
|
||||||
log_level = getattr(LogLevel, log_level.upper())
|
|
||||||
except AttributeError as ae:
|
|
||||||
raise ValueError(f"Invalid log level: {log_level}") from ae
|
|
||||||
|
|
||||||
global _LOG_LEVEL
|
global _LOG_LEVEL
|
||||||
_LOG_LEVEL = log_level
|
_LOG_LEVEL = log_level
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"""Custom Exceptions."""
|
"""Custom Exceptions."""
|
||||||
|
|
||||||
from typing import Any, NoReturn
|
from typing import Any
|
||||||
|
|
||||||
|
|
||||||
class ReflexError(Exception):
|
class ReflexError(Exception):
|
||||||
@ -75,6 +75,30 @@ class VarAttributeError(ReflexError, AttributeError):
|
|||||||
"""Custom AttributeError for var related errors."""
|
"""Custom AttributeError for var related errors."""
|
||||||
|
|
||||||
|
|
||||||
|
class UntypedComputedVarError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for untyped computed var errors."""
|
||||||
|
|
||||||
|
def __init__(self, var_name):
|
||||||
|
"""Initialize the UntypedComputedVarError.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
var_name: The name of the computed var.
|
||||||
|
"""
|
||||||
|
super().__init__(f"Computed var '{var_name}' must have a type annotation.")
|
||||||
|
|
||||||
|
|
||||||
|
class MissingAnnotationError(ReflexError, TypeError):
|
||||||
|
"""Custom TypeError for missing annotations."""
|
||||||
|
|
||||||
|
def __init__(self, var_name):
|
||||||
|
"""Initialize the MissingAnnotationError.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
var_name: The name of the var.
|
||||||
|
"""
|
||||||
|
super().__init__(f"Var '{var_name}' must have a type annotation.")
|
||||||
|
|
||||||
|
|
||||||
class UploadValueError(ReflexError, ValueError):
|
class UploadValueError(ReflexError, ValueError):
|
||||||
"""Custom ValueError for upload related errors."""
|
"""Custom ValueError for upload related errors."""
|
||||||
|
|
||||||
@ -111,11 +135,11 @@ class MatchTypeError(ReflexError, TypeError):
|
|||||||
"""Raised when the return types of match cases are different."""
|
"""Raised when the return types of match cases are different."""
|
||||||
|
|
||||||
|
|
||||||
class EventHandlerArgTypeMismatch(ReflexError, TypeError):
|
class EventHandlerArgTypeMismatchError(ReflexError, TypeError):
|
||||||
"""Raised when the annotations of args accepted by an EventHandler differs from the spec of the event trigger."""
|
"""Raised when the annotations of args accepted by an EventHandler differs from the spec of the event trigger."""
|
||||||
|
|
||||||
|
|
||||||
class EventFnArgMismatch(ReflexError, TypeError):
|
class EventFnArgMismatchError(ReflexError, TypeError):
|
||||||
"""Raised when the number of args required by an event handler is more than provided by the event trigger."""
|
"""Raised when the number of args required by an event handler is more than provided by the event trigger."""
|
||||||
|
|
||||||
|
|
||||||
@ -186,29 +210,27 @@ class StateMismatchError(ReflexError, ValueError):
|
|||||||
class SystemPackageMissingError(ReflexError):
|
class SystemPackageMissingError(ReflexError):
|
||||||
"""Raised when a system package is missing."""
|
"""Raised when a system package is missing."""
|
||||||
|
|
||||||
|
def __init__(self, package: str):
|
||||||
|
"""Initialize the SystemPackageMissingError.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package: The missing package.
|
||||||
|
"""
|
||||||
|
from reflex.constants import IS_MACOS
|
||||||
|
|
||||||
|
extra = (
|
||||||
|
f" You can do so by running 'brew install {package}'." if IS_MACOS else ""
|
||||||
|
)
|
||||||
|
super().__init__(
|
||||||
|
f"System package '{package}' is missing."
|
||||||
|
f" Please install it through your system package manager.{extra}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class EventDeserializationError(ReflexError, ValueError):
|
class EventDeserializationError(ReflexError, ValueError):
|
||||||
"""Raised when an event cannot be deserialized."""
|
"""Raised when an event cannot be deserialized."""
|
||||||
|
|
||||||
|
|
||||||
def raise_system_package_missing_error(package: str) -> NoReturn:
|
|
||||||
"""Raise a SystemPackageMissingError.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
package: The name of the missing system package.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
SystemPackageMissingError: The raised exception.
|
|
||||||
"""
|
|
||||||
from reflex.constants import IS_MACOS
|
|
||||||
|
|
||||||
raise SystemPackageMissingError(
|
|
||||||
f"System package '{package}' is missing."
|
|
||||||
" Please install it through your system package manager."
|
|
||||||
+ (f" You can do so by running 'brew install {package}'." if IS_MACOS else "")
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidLockWarningThresholdError(ReflexError):
|
class InvalidLockWarningThresholdError(ReflexError):
|
||||||
"""Raised when an invalid lock warning threshold is provided."""
|
"""Raised when an invalid lock warning threshold is provided."""
|
||||||
|
|
||||||
|
@ -240,25 +240,28 @@ def run_backend(
|
|||||||
run_uvicorn_backend(host, port, loglevel)
|
run_uvicorn_backend(host, port, loglevel)
|
||||||
|
|
||||||
|
|
||||||
def get_reload_dirs() -> list[str]:
|
def get_reload_dirs() -> list[Path]:
|
||||||
"""Get the reload directories for the backend.
|
"""Get the reload directories for the backend.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The reload directories for the backend.
|
The reload directories for the backend.
|
||||||
"""
|
"""
|
||||||
config = get_config()
|
config = get_config()
|
||||||
reload_dirs = [config.app_name]
|
reload_dirs = [Path(config.app_name)]
|
||||||
if config.app_module is not None and config.app_module.__file__:
|
if config.app_module is not None and config.app_module.__file__:
|
||||||
module_path = Path(config.app_module.__file__).resolve().parent
|
module_path = Path(config.app_module.__file__).resolve().parent
|
||||||
|
|
||||||
while module_path.parent.name:
|
while module_path.parent.name:
|
||||||
for parent_file in module_path.parent.iterdir():
|
if any(
|
||||||
if parent_file == "__init__.py":
|
sibling_file.name == "__init__.py"
|
||||||
# go up a level to find dir without `__init__.py`
|
for sibling_file in module_path.parent.iterdir()
|
||||||
module_path = module_path.parent
|
):
|
||||||
break
|
# go up a level to find dir without `__init__.py`
|
||||||
|
module_path = module_path.parent
|
||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
reload_dirs.append(str(module_path))
|
|
||||||
|
reload_dirs = [module_path]
|
||||||
return reload_dirs
|
return reload_dirs
|
||||||
|
|
||||||
|
|
||||||
@ -278,7 +281,7 @@ def run_uvicorn_backend(host, port, loglevel: LogLevel):
|
|||||||
port=port,
|
port=port,
|
||||||
log_level=loglevel.value,
|
log_level=loglevel.value,
|
||||||
reload=True,
|
reload=True,
|
||||||
reload_dirs=get_reload_dirs(),
|
reload_dirs=list(map(str, get_reload_dirs())),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -526,48 +529,3 @@ def is_prod_mode() -> bool:
|
|||||||
"""
|
"""
|
||||||
current_mode = environment.REFLEX_ENV_MODE.get()
|
current_mode = environment.REFLEX_ENV_MODE.get()
|
||||||
return current_mode == constants.Env.PROD
|
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()
|
|
||||||
|
@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, Any, List, Optional, Union
|
|||||||
from reflex import constants
|
from reflex import constants
|
||||||
from reflex.constants.state import FRONTEND_EVENT_STATE
|
from reflex.constants.state import FRONTEND_EVENT_STATE
|
||||||
from reflex.utils import exceptions
|
from reflex.utils import exceptions
|
||||||
from reflex.utils.console import deprecate
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from reflex.components.component import ComponentStyle
|
from reflex.components.component import ComponentStyle
|
||||||
@ -502,37 +501,6 @@ if TYPE_CHECKING:
|
|||||||
from reflex.vars import Var
|
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(
|
def format_queue_events(
|
||||||
events: EventType | None = None,
|
events: EventType | None = None,
|
||||||
args_spec: Optional[ArgsSpec] = None,
|
args_spec: Optional[ArgsSpec] = None,
|
||||||
|
@ -38,7 +38,7 @@ from reflex.config import Config, environment, get_config
|
|||||||
from reflex.utils import console, net, path_ops, processes, redir
|
from reflex.utils import console, net, path_ops, processes, redir
|
||||||
from reflex.utils.exceptions import (
|
from reflex.utils.exceptions import (
|
||||||
GeneratedCodeHasNoFunctionDefs,
|
GeneratedCodeHasNoFunctionDefs,
|
||||||
raise_system_package_missing_error,
|
SystemPackageMissingError,
|
||||||
)
|
)
|
||||||
from reflex.utils.format import format_library_name
|
from reflex.utils.format import format_library_name
|
||||||
from reflex.utils.registry import _get_npm_registry
|
from reflex.utils.registry import _get_npm_registry
|
||||||
@ -86,18 +86,6 @@ def get_web_dir() -> Path:
|
|||||||
return environment.REFLEX_WEB_WORKDIR.get()
|
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):
|
def check_latest_package_version(package_name: str):
|
||||||
"""Check if the latest version of the package is installed.
|
"""Check if the latest version of the package is installed.
|
||||||
|
|
||||||
@ -120,8 +108,6 @@ def check_latest_package_version(package_name: str):
|
|||||||
console.warn(
|
console.warn(
|
||||||
f"Your version ({current_version}) of {package_name} is out of date. Upgrade to {latest_version} with 'pip install {package_name} --upgrade'"
|
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:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -885,7 +871,11 @@ def install_node():
|
|||||||
|
|
||||||
|
|
||||||
def install_bun():
|
def install_bun():
|
||||||
"""Install bun onto the user's system."""
|
"""Install bun onto the user's system.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SystemPackageMissingError: If "unzip" is missing.
|
||||||
|
"""
|
||||||
win_supported = is_windows_bun_supported()
|
win_supported = is_windows_bun_supported()
|
||||||
one_drive_in_path = windows_check_onedrive_in_path()
|
one_drive_in_path = windows_check_onedrive_in_path()
|
||||||
if constants.IS_WINDOWS and (not win_supported or one_drive_in_path):
|
if constants.IS_WINDOWS and (not win_supported or one_drive_in_path):
|
||||||
@ -924,7 +914,7 @@ def install_bun():
|
|||||||
else:
|
else:
|
||||||
unzip_path = path_ops.which("unzip")
|
unzip_path = path_ops.which("unzip")
|
||||||
if unzip_path is None:
|
if unzip_path is None:
|
||||||
raise_system_package_missing_error("unzip")
|
raise SystemPackageMissingError("unzip")
|
||||||
|
|
||||||
# Run the bun install script.
|
# Run the bun install script.
|
||||||
download_and_run(
|
download_and_run(
|
||||||
|
@ -122,7 +122,7 @@ def _prepare_event(event: str, **kwargs) -> dict:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
if UTC is None:
|
if UTC is None:
|
||||||
# for python 3.9 & 3.10
|
# for python 3.10
|
||||||
stamp = datetime.utcnow().isoformat()
|
stamp = datetime.utcnow().isoformat()
|
||||||
else:
|
else:
|
||||||
# for python 3.11 & 3.12
|
# for python 3.11 & 3.12
|
||||||
|
@ -46,6 +46,7 @@ from reflex.base import Base
|
|||||||
from reflex.constants.compiler import Hooks
|
from reflex.constants.compiler import Hooks
|
||||||
from reflex.utils import console, exceptions, imports, serializers, types
|
from reflex.utils import console, exceptions, imports, serializers, types
|
||||||
from reflex.utils.exceptions import (
|
from reflex.utils.exceptions import (
|
||||||
|
UntypedComputedVarError,
|
||||||
VarAttributeError,
|
VarAttributeError,
|
||||||
VarDependencyError,
|
VarDependencyError,
|
||||||
VarTypeError,
|
VarTypeError,
|
||||||
@ -545,52 +546,21 @@ class Var(Generic[VAR_TYPE]):
|
|||||||
def create(
|
def create(
|
||||||
cls,
|
cls,
|
||||||
value: Any,
|
value: Any,
|
||||||
_var_is_local: bool | None = None,
|
|
||||||
_var_is_string: bool | None = None,
|
|
||||||
_var_data: VarData | None = None,
|
_var_data: VarData | None = None,
|
||||||
) -> Var:
|
) -> Var:
|
||||||
"""Create a var from a value.
|
"""Create a var from a value.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
value: The value to create the var from.
|
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.
|
_var_data: Additional hooks and imports associated with the Var.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The var.
|
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 the value is already a var, do nothing.
|
||||||
if isinstance(value, Var):
|
if isinstance(value, Var):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
# Try to pull the imports and hooks from contained values.
|
|
||||||
if not isinstance(value, str):
|
|
||||||
return LiteralVar.create(value, _var_data=_var_data)
|
|
||||||
|
|
||||||
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)
|
return LiteralVar.create(value, _var_data=_var_data)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@ -1863,19 +1833,14 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
TypeError: If the computed var dependencies are not Var instances or var names.
|
TypeError: If the computed var dependencies are not Var instances or var names.
|
||||||
|
UntypedComputedVarError: If the computed var is untyped.
|
||||||
"""
|
"""
|
||||||
hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
|
hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
|
||||||
"return", Any
|
"return", Any
|
||||||
)
|
)
|
||||||
|
|
||||||
if hint is Any:
|
if hint is Any:
|
||||||
console.deprecate(
|
raise UntypedComputedVarError(var_name=fget.__name__)
|
||||||
"untyped-computed-var",
|
|
||||||
"ComputedVar should have a return type annotation.",
|
|
||||||
"0.6.5",
|
|
||||||
"0.7.0",
|
|
||||||
)
|
|
||||||
|
|
||||||
kwargs.setdefault("_js_expr", fget.__name__)
|
kwargs.setdefault("_js_expr", fget.__name__)
|
||||||
kwargs.setdefault("_var_type", hint)
|
kwargs.setdefault("_var_type", hint)
|
||||||
|
|
||||||
@ -1948,6 +1913,7 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|||||||
"_var_data": kwargs.pop(
|
"_var_data": kwargs.pop(
|
||||||
"_var_data", VarData.merge(self._var_data, merge_var_data)
|
"_var_data", VarData.merge(self._var_data, merge_var_data)
|
||||||
),
|
),
|
||||||
|
"return_type": kwargs.pop("return_type", self._var_type),
|
||||||
}
|
}
|
||||||
|
|
||||||
if kwargs:
|
if kwargs:
|
||||||
@ -2082,12 +2048,9 @@ class ComputedVar(Var[RETURN_TYPE]):
|
|||||||
value = getattr(instance, self._cache_attr)
|
value = getattr(instance, self._cache_attr)
|
||||||
|
|
||||||
if not _isinstance(value, self._var_type):
|
if not _isinstance(value, self._var_type):
|
||||||
console.deprecate(
|
console.error(
|
||||||
"mismatched-computed-var-return",
|
f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
|
||||||
f"Computed var {type(instance).__name__}.{self._js_expr} returned value of type {type(value)}, "
|
f" type '{self._var_type}', got '{type(value)}'."
|
||||||
f"expected {self._var_type}. This might cause unexpected behavior.",
|
|
||||||
"0.6.5",
|
|
||||||
"0.7.0",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
FROM python:3.9
|
FROM python:3.10
|
||||||
|
|
||||||
ARG USERNAME=kerrigan
|
ARG USERNAME=kerrigan
|
||||||
RUN useradd -m $USERNAME
|
RUN useradd -m $USERNAME
|
||||||
|
@ -16,7 +16,7 @@ from .utils import SessionStorage
|
|||||||
def CallScript():
|
def CallScript():
|
||||||
"""A test app for browser javascript integration."""
|
"""A test app for browser javascript integration."""
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Union
|
from typing import Optional, Union
|
||||||
|
|
||||||
import reflex as rx
|
import reflex as rx
|
||||||
|
|
||||||
@ -43,15 +43,17 @@ def CallScript():
|
|||||||
external_scripts = inline_scripts.replace("inline", "external")
|
external_scripts = inline_scripts.replace("inline", "external")
|
||||||
|
|
||||||
class CallScriptState(rx.State):
|
class CallScriptState(rx.State):
|
||||||
results: List[Optional[Union[str, Dict, List]]] = []
|
results: rx.Field[list[Optional[Union[str, dict, list]]]] = rx.field([])
|
||||||
inline_counter: int = 0
|
inline_counter: rx.Field[int] = rx.field(0)
|
||||||
external_counter: int = 0
|
external_counter: rx.Field[int] = rx.field(0)
|
||||||
value: str = "Initial"
|
value: str = "Initial"
|
||||||
last_result: str = ""
|
last_result: int = 0
|
||||||
|
|
||||||
|
@rx.event
|
||||||
def call_script_callback(self, result):
|
def call_script_callback(self, result):
|
||||||
self.results.append(result)
|
self.results.append(result)
|
||||||
|
|
||||||
|
@rx.event
|
||||||
def call_script_callback_other_arg(self, result, other_arg):
|
def call_script_callback_other_arg(self, result, other_arg):
|
||||||
self.results.append([other_arg, result])
|
self.results.append([other_arg, result])
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ def CallScript():
|
|||||||
def call_script_inline_return_lambda(self):
|
def call_script_inline_return_lambda(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
"inline2()",
|
"inline2()",
|
||||||
callback=lambda result: CallScriptState.call_script_callback_other_arg( # type: ignore
|
callback=lambda result: CallScriptState.call_script_callback_other_arg(
|
||||||
result, "lambda"
|
result, "lambda"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -100,7 +102,7 @@ def CallScript():
|
|||||||
def get_inline_counter(self):
|
def get_inline_counter(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
"inline_counter",
|
"inline_counter",
|
||||||
callback=CallScriptState.set_inline_counter, # type: ignore
|
callback=CallScriptState.setvar("inline_counter"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
@ -139,7 +141,7 @@ def CallScript():
|
|||||||
def call_script_external_return_lambda(self):
|
def call_script_external_return_lambda(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
"external2()",
|
"external2()",
|
||||||
callback=lambda result: CallScriptState.call_script_callback_other_arg( # type: ignore
|
callback=lambda result: CallScriptState.call_script_callback_other_arg(
|
||||||
result, "lambda"
|
result, "lambda"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -148,28 +150,28 @@ def CallScript():
|
|||||||
def get_external_counter(self):
|
def get_external_counter(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
"external_counter",
|
"external_counter",
|
||||||
callback=CallScriptState.set_external_counter, # type: ignore
|
callback=CallScriptState.setvar("external_counter"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def call_with_var_f_string(self):
|
def call_with_var_f_string(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
f"{rx.Var('inline_counter')} + {rx.Var('external_counter')}",
|
f"{rx.Var('inline_counter')} + {rx.Var('external_counter')}",
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def call_with_var_str_cast(self):
|
def call_with_var_str_cast(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}",
|
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}",
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
def call_with_var_f_string_wrapped(self):
|
def call_with_var_f_string_wrapped(self):
|
||||||
return rx.call_script(
|
return rx.call_script(
|
||||||
rx.Var(f"{rx.Var('inline_counter')} + {rx.Var('external_counter')}"),
|
rx.Var(f"{rx.Var('inline_counter')} + {rx.Var('external_counter')}"),
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
@ -178,7 +180,7 @@ def CallScript():
|
|||||||
rx.Var(
|
rx.Var(
|
||||||
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}"
|
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}"
|
||||||
),
|
),
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@rx.event
|
@rx.event
|
||||||
@ -193,17 +195,17 @@ def CallScript():
|
|||||||
def index():
|
def index():
|
||||||
return rx.vstack(
|
return rx.vstack(
|
||||||
rx.input(
|
rx.input(
|
||||||
value=CallScriptState.inline_counter.to(str), # type: ignore
|
value=CallScriptState.inline_counter.to(str),
|
||||||
id="inline_counter",
|
id="inline_counter",
|
||||||
read_only=True,
|
read_only=True,
|
||||||
),
|
),
|
||||||
rx.input(
|
rx.input(
|
||||||
value=CallScriptState.external_counter.to(str), # type: ignore
|
value=CallScriptState.external_counter.to(str),
|
||||||
id="external_counter",
|
id="external_counter",
|
||||||
read_only=True,
|
read_only=True,
|
||||||
),
|
),
|
||||||
rx.text_area(
|
rx.text_area(
|
||||||
value=CallScriptState.results.to_string(), # type: ignore
|
value=CallScriptState.results.to_string(),
|
||||||
id="results",
|
id="results",
|
||||||
read_only=True,
|
read_only=True,
|
||||||
),
|
),
|
||||||
@ -273,7 +275,7 @@ def CallScript():
|
|||||||
CallScriptState.value,
|
CallScriptState.value,
|
||||||
on_click=rx.call_script(
|
on_click=rx.call_script(
|
||||||
"'updated'",
|
"'updated'",
|
||||||
callback=CallScriptState.set_value, # type: ignore
|
callback=CallScriptState.setvar("value"),
|
||||||
),
|
),
|
||||||
id="update_value",
|
id="update_value",
|
||||||
),
|
),
|
||||||
@ -282,7 +284,7 @@ def CallScript():
|
|||||||
value=CallScriptState.last_result,
|
value=CallScriptState.last_result,
|
||||||
id="last_result",
|
id="last_result",
|
||||||
read_only=True,
|
read_only=True,
|
||||||
on_click=CallScriptState.set_last_result(""), # type: ignore
|
on_click=CallScriptState.setvar("last_result", 0),
|
||||||
),
|
),
|
||||||
rx.button(
|
rx.button(
|
||||||
"call_with_var_f_string",
|
"call_with_var_f_string",
|
||||||
@ -308,7 +310,7 @@ def CallScript():
|
|||||||
"call_with_var_f_string_inline",
|
"call_with_var_f_string_inline",
|
||||||
on_click=rx.call_script(
|
on_click=rx.call_script(
|
||||||
f"{rx.Var('inline_counter')} + {CallScriptState.last_result}",
|
f"{rx.Var('inline_counter')} + {CallScriptState.last_result}",
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
),
|
),
|
||||||
id="call_with_var_f_string_inline",
|
id="call_with_var_f_string_inline",
|
||||||
),
|
),
|
||||||
@ -316,7 +318,7 @@ def CallScript():
|
|||||||
"call_with_var_str_cast_inline",
|
"call_with_var_str_cast_inline",
|
||||||
on_click=rx.call_script(
|
on_click=rx.call_script(
|
||||||
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}",
|
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}",
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
),
|
),
|
||||||
id="call_with_var_str_cast_inline",
|
id="call_with_var_str_cast_inline",
|
||||||
),
|
),
|
||||||
@ -326,7 +328,7 @@ def CallScript():
|
|||||||
rx.Var(
|
rx.Var(
|
||||||
f"{rx.Var('inline_counter')} + {CallScriptState.last_result}"
|
f"{rx.Var('inline_counter')} + {CallScriptState.last_result}"
|
||||||
),
|
),
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
),
|
),
|
||||||
id="call_with_var_f_string_wrapped_inline",
|
id="call_with_var_f_string_wrapped_inline",
|
||||||
),
|
),
|
||||||
@ -336,7 +338,7 @@ def CallScript():
|
|||||||
rx.Var(
|
rx.Var(
|
||||||
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}"
|
f"{rx.Var('inline_counter')!s} + {rx.Var('external_counter')!s}"
|
||||||
),
|
),
|
||||||
callback=CallScriptState.set_last_result, # type: ignore
|
callback=CallScriptState.setvar("last_result"),
|
||||||
),
|
),
|
||||||
id="call_with_var_str_cast_wrapped_inline",
|
id="call_with_var_str_cast_wrapped_inline",
|
||||||
),
|
),
|
||||||
@ -483,7 +485,7 @@ def test_call_script_w_var(
|
|||||||
"""
|
"""
|
||||||
assert_token(driver)
|
assert_token(driver)
|
||||||
last_result = driver.find_element(By.ID, "last_result")
|
last_result = driver.find_element(By.ID, "last_result")
|
||||||
assert last_result.get_attribute("value") == ""
|
assert last_result.get_attribute("value") == "0"
|
||||||
|
|
||||||
inline_return_button = driver.find_element(By.ID, "inline_return")
|
inline_return_button = driver.find_element(By.ID, "inline_return")
|
||||||
|
|
||||||
|
@ -33,18 +33,18 @@ def ClientSide():
|
|||||||
class ClientSideSubState(ClientSideState):
|
class ClientSideSubState(ClientSideState):
|
||||||
# cookies with default settings
|
# cookies with default settings
|
||||||
c1: str = rx.Cookie()
|
c1: str = rx.Cookie()
|
||||||
c2: rx.Cookie = "c2 default" # type: ignore
|
c2: str = rx.Cookie("c2 default")
|
||||||
|
|
||||||
# cookies with custom settings
|
# cookies with custom settings
|
||||||
c3: str = rx.Cookie(max_age=2) # expires after 2 second
|
c3: str = rx.Cookie(max_age=2) # expires after 2 second
|
||||||
c4: rx.Cookie = rx.Cookie(same_site="strict")
|
c4: str = rx.Cookie(same_site="strict")
|
||||||
c5: str = rx.Cookie(path="/foo/") # only accessible on `/foo/`
|
c5: str = rx.Cookie(path="/foo/") # only accessible on `/foo/`
|
||||||
c6: str = rx.Cookie(name="c6")
|
c6: str = rx.Cookie(name="c6")
|
||||||
c7: str = rx.Cookie("c7 default")
|
c7: str = rx.Cookie("c7 default")
|
||||||
|
|
||||||
# local storage with default settings
|
# local storage with default settings
|
||||||
l1: str = rx.LocalStorage()
|
l1: str = rx.LocalStorage()
|
||||||
l2: rx.LocalStorage = "l2 default" # type: ignore
|
l2: str = rx.LocalStorage("l2 default")
|
||||||
|
|
||||||
# local storage with custom settings
|
# local storage with custom settings
|
||||||
l3: str = rx.LocalStorage(name="l3")
|
l3: str = rx.LocalStorage(name="l3")
|
||||||
@ -56,7 +56,7 @@ def ClientSide():
|
|||||||
|
|
||||||
# Session storage
|
# Session storage
|
||||||
s1: str = rx.SessionStorage()
|
s1: str = rx.SessionStorage()
|
||||||
s2: rx.SessionStorage = "s2 default" # type: ignore
|
s2: str = rx.SessionStorage("s2 default")
|
||||||
s3: str = rx.SessionStorage(name="s3")
|
s3: str = rx.SessionStorage(name="s3")
|
||||||
|
|
||||||
def set_l6(self, my_param: str):
|
def set_l6(self, my_param: str):
|
||||||
@ -87,13 +87,13 @@ def ClientSide():
|
|||||||
rx.input(
|
rx.input(
|
||||||
placeholder="state var",
|
placeholder="state var",
|
||||||
value=ClientSideState.state_var,
|
value=ClientSideState.state_var,
|
||||||
on_change=ClientSideState.set_state_var, # type: ignore
|
on_change=ClientSideState.setvar("state_var"),
|
||||||
id="state_var",
|
id="state_var",
|
||||||
),
|
),
|
||||||
rx.input(
|
rx.input(
|
||||||
placeholder="input value",
|
placeholder="input value",
|
||||||
value=ClientSideState.input_value,
|
value=ClientSideState.input_value,
|
||||||
on_change=ClientSideState.set_input_value, # type: ignore
|
on_change=ClientSideState.setvar("input_value"),
|
||||||
id="input_value",
|
id="input_value",
|
||||||
),
|
),
|
||||||
rx.button(
|
rx.button(
|
||||||
|
@ -37,19 +37,6 @@ def test_shared_asset() -> None:
|
|||||||
assert not Path(Path.cwd() / "assets/external").exists()
|
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(
|
@pytest.mark.parametrize(
|
||||||
"path,shared",
|
"path,shared",
|
||||||
[
|
[
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
"""Data display component tests fixtures."""
|
"""Data display component tests fixtures."""
|
||||||
|
|
||||||
from typing import List
|
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
@ -54,11 +52,11 @@ def data_table_state3():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class DataTableState(BaseState):
|
class DataTableState(BaseState):
|
||||||
_data: List = []
|
_data: list = []
|
||||||
_columns: List = ["col1", "col2"]
|
_columns: list = ["col1", "col2"]
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def data(self) -> List:
|
def data(self) -> list:
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
@ -77,15 +75,15 @@ def data_table_state4():
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
class DataTableState(BaseState):
|
class DataTableState(BaseState):
|
||||||
_data: List = []
|
_data: list = []
|
||||||
_columns: List = ["col1", "col2"]
|
_columns: list[str] = ["col1", "col2"]
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def data(self):
|
def data(self):
|
||||||
return self._data
|
return self._data
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def columns(self) -> List:
|
def columns(self) -> list:
|
||||||
return self._columns
|
return self._columns
|
||||||
|
|
||||||
return DataTableState
|
return DataTableState
|
||||||
|
@ -4,6 +4,7 @@ import pytest
|
|||||||
import reflex as rx
|
import reflex as rx
|
||||||
from reflex.components.gridjs.datatable import DataTable
|
from reflex.components.gridjs.datatable import DataTable
|
||||||
from reflex.utils import types
|
from reflex.utils import types
|
||||||
|
from reflex.utils.exceptions import UntypedComputedVarError
|
||||||
from reflex.utils.serializers import serialize, serialize_dataframe
|
from reflex.utils.serializers import serialize, serialize_dataframe
|
||||||
|
|
||||||
|
|
||||||
@ -75,17 +76,17 @@ def test_invalid_props(props):
|
|||||||
[
|
[
|
||||||
(
|
(
|
||||||
"data_table_state2",
|
"data_table_state2",
|
||||||
"Annotation of the computed var assigned to the data field should be provided.",
|
"Computed var 'data' must have a type annotation.",
|
||||||
True,
|
True,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"data_table_state3",
|
"data_table_state3",
|
||||||
"Annotation of the computed var assigned to the column field should be provided.",
|
"Computed var 'columns' must have a type annotation.",
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"data_table_state4",
|
"data_table_state4",
|
||||||
"Annotation of the computed var assigned to the data field should be provided.",
|
"Computed var 'data' must have a type annotation.",
|
||||||
False,
|
False,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -99,7 +100,7 @@ def test_computed_var_without_annotation(fixture, request, err_msg, is_data_fram
|
|||||||
err_msg: expected error message.
|
err_msg: expected error message.
|
||||||
is_data_frame: whether data field is a pandas dataframe.
|
is_data_frame: whether data field is a pandas dataframe.
|
||||||
"""
|
"""
|
||||||
with pytest.raises(ValueError) as err:
|
with pytest.raises(UntypedComputedVarError) as err:
|
||||||
if is_data_frame:
|
if is_data_frame:
|
||||||
DataTable.create(data=request.getfixturevalue(fixture).data)
|
DataTable.create(data=request.getfixturevalue(fixture).data)
|
||||||
else:
|
else:
|
||||||
|
@ -19,6 +19,7 @@ from reflex.constants import EventTriggers
|
|||||||
from reflex.event import (
|
from reflex.event import (
|
||||||
EventChain,
|
EventChain,
|
||||||
EventHandler,
|
EventHandler,
|
||||||
|
JavascriptInputEvent,
|
||||||
input_event,
|
input_event,
|
||||||
no_args_event_spec,
|
no_args_event_spec,
|
||||||
parse_args_spec,
|
parse_args_spec,
|
||||||
@ -27,7 +28,11 @@ from reflex.event import (
|
|||||||
from reflex.state import BaseState
|
from reflex.state import BaseState
|
||||||
from reflex.style import Style
|
from reflex.style import Style
|
||||||
from reflex.utils import imports
|
from reflex.utils import imports
|
||||||
from reflex.utils.exceptions import ChildrenTypeError, EventFnArgMismatch
|
from reflex.utils.exceptions import (
|
||||||
|
ChildrenTypeError,
|
||||||
|
EventFnArgMismatchError,
|
||||||
|
EventHandlerArgTypeMismatchError,
|
||||||
|
)
|
||||||
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import LiteralVar, Var
|
from reflex.vars.base import LiteralVar, Var
|
||||||
@ -94,11 +99,14 @@ def component2() -> Type[Component]:
|
|||||||
A test component.
|
A test component.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def on_prop_event_spec(e0: Any):
|
||||||
|
return [e0]
|
||||||
|
|
||||||
class TestComponent2(Component):
|
class TestComponent2(Component):
|
||||||
# A test list prop.
|
# A test list prop.
|
||||||
arr: Var[List[str]]
|
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]:
|
def get_event_triggers(self) -> Dict[str, Any]:
|
||||||
"""Test controlled triggers.
|
"""Test controlled triggers.
|
||||||
@ -818,10 +826,14 @@ def test_component_create_unpack_tuple_child(test_component, element, expected):
|
|||||||
assert fragment_wrapper.render() == expected
|
assert fragment_wrapper.render() == expected
|
||||||
|
|
||||||
|
|
||||||
|
class _Obj(Base):
|
||||||
|
custom: int = 0
|
||||||
|
|
||||||
|
|
||||||
class C1State(BaseState):
|
class C1State(BaseState):
|
||||||
"""State for testing C1 component."""
|
"""State for testing C1 component."""
|
||||||
|
|
||||||
def mock_handler(self, _e, _bravo, _charlie):
|
def mock_handler(self, _e: JavascriptInputEvent, _bravo: dict, _charlie: _Obj):
|
||||||
"""Mock handler."""
|
"""Mock handler."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -829,10 +841,12 @@ class C1State(BaseState):
|
|||||||
def test_component_event_trigger_arbitrary_args():
|
def test_component_event_trigger_arbitrary_args():
|
||||||
"""Test that we can define arbitrary types for the args of an event trigger."""
|
"""Test that we can define arbitrary types for the args of an event trigger."""
|
||||||
|
|
||||||
class Obj(Base):
|
def on_foo_spec(
|
||||||
custom: int = 0
|
_e: Var[JavascriptInputEvent],
|
||||||
|
alpha: Var[str],
|
||||||
def on_foo_spec(_e, alpha: str, bravo: Dict[str, Any], charlie: Obj):
|
bravo: dict[str, Any],
|
||||||
|
charlie: Var[_Obj],
|
||||||
|
):
|
||||||
return [_e.target.value, bravo["nested"], charlie.custom + 42]
|
return [_e.target.value, bravo["nested"], charlie.custom + 42]
|
||||||
|
|
||||||
class C1(Component):
|
class C1(Component):
|
||||||
@ -845,13 +859,7 @@ def test_component_event_trigger_arbitrary_args():
|
|||||||
"on_foo": on_foo_spec,
|
"on_foo": on_foo_spec,
|
||||||
}
|
}
|
||||||
|
|
||||||
comp = C1.create(on_foo=C1State.mock_handler)
|
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], ({ }))))}"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_create_custom_component(my_component):
|
def test_create_custom_component(my_component):
|
||||||
@ -908,30 +916,29 @@ def test_invalid_event_handler_args(component2, test_state):
|
|||||||
test_state: A test state.
|
test_state: A test state.
|
||||||
"""
|
"""
|
||||||
# EventHandler args must match
|
# EventHandler args must match
|
||||||
with pytest.raises(EventFnArgMismatch):
|
with pytest.raises(EventFnArgMismatchError):
|
||||||
component2.create(on_click=test_state.do_something_arg)
|
component2.create(on_click=test_state.do_something_arg)
|
||||||
|
|
||||||
# Multiple EventHandler args: all must match
|
# Multiple EventHandler args: all must match
|
||||||
with pytest.raises(EventFnArgMismatch):
|
with pytest.raises(EventFnArgMismatchError):
|
||||||
component2.create(
|
component2.create(
|
||||||
on_click=[test_state.do_something_arg, test_state.do_something]
|
on_click=[test_state.do_something_arg, test_state.do_something]
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable when 0.7.0 happens
|
|
||||||
# # Event Handler types must match
|
# # Event Handler types must match
|
||||||
# with pytest.raises(EventHandlerArgTypeMismatch):
|
with pytest.raises(EventHandlerArgTypeMismatchError):
|
||||||
# component2.create(
|
component2.create(
|
||||||
# on_user_visited_count_changed=test_state.do_something_with_bool # noqa: ERA001 RUF100
|
on_user_visited_count_changed=test_state.do_something_with_bool
|
||||||
# ) # noqa: ERA001 RUF100
|
)
|
||||||
# with pytest.raises(EventHandlerArgTypeMismatch):
|
with pytest.raises(EventHandlerArgTypeMismatchError):
|
||||||
# component2.create(on_user_list_changed=test_state.do_something_with_int) #noqa: ERA001
|
component2.create(on_user_list_changed=test_state.do_something_with_int)
|
||||||
# with pytest.raises(EventHandlerArgTypeMismatch):
|
with pytest.raises(EventHandlerArgTypeMismatchError):
|
||||||
# component2.create(on_user_list_changed=test_state.do_something_with_list_int) #noqa: ERA001
|
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_int)
|
||||||
# component2.create(on_open=test_state.do_something_with_bool) #noqa: ERA001
|
component2.create(on_open=test_state.do_something_with_bool)
|
||||||
# component2.create(on_user_visited_count_changed=test_state.do_something_with_int) #noqa: ERA001
|
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) #noqa: ERA001
|
component2.create(on_user_list_changed=test_state.do_something_with_list_str)
|
||||||
|
|
||||||
# lambda cannot return weird values.
|
# lambda cannot return weird values.
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
@ -944,15 +951,15 @@ def test_invalid_event_handler_args(component2, test_state):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# lambda signature must match event trigger.
|
# lambda signature must match event trigger.
|
||||||
with pytest.raises(EventFnArgMismatch):
|
with pytest.raises(EventFnArgMismatchError):
|
||||||
component2.create(on_click=lambda _: test_state.do_something_arg(1))
|
component2.create(on_click=lambda _: test_state.do_something_arg(1))
|
||||||
|
|
||||||
# lambda returning EventHandler must match spec
|
# lambda returning EventHandler must match spec
|
||||||
with pytest.raises(EventFnArgMismatch):
|
with pytest.raises(EventFnArgMismatchError):
|
||||||
component2.create(on_click=lambda: test_state.do_something_arg)
|
component2.create(on_click=lambda: test_state.do_something_arg)
|
||||||
|
|
||||||
# Mixed EventSpec and EventHandler must match spec.
|
# Mixed EventSpec and EventHandler must match spec.
|
||||||
with pytest.raises(EventFnArgMismatch):
|
with pytest.raises(EventFnArgMismatchError):
|
||||||
component2.create(
|
component2.create(
|
||||||
on_click=lambda: [
|
on_click=lambda: [
|
||||||
test_state.do_something_arg(1),
|
test_state.do_something_arg(1),
|
||||||
@ -1801,21 +1808,15 @@ def test_custom_component_declare_event_handlers_in_fields():
|
|||||||
"""
|
"""
|
||||||
return {
|
return {
|
||||||
**super().get_event_triggers(),
|
**super().get_event_triggers(),
|
||||||
"on_a": lambda e0: [e0],
|
|
||||||
"on_b": input_event,
|
"on_b": input_event,
|
||||||
"on_c": lambda e0: [],
|
|
||||||
"on_d": lambda: [],
|
"on_d": lambda: [],
|
||||||
"on_e": lambda: [],
|
"on_e": lambda: [],
|
||||||
"on_f": lambda a, b, c: [c, b, a],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestComponent(Component):
|
class TestComponent(Component):
|
||||||
on_a: EventHandler[lambda e0: [e0]]
|
|
||||||
on_b: EventHandler[input_event]
|
on_b: EventHandler[input_event]
|
||||||
on_c: EventHandler[no_args_event_spec]
|
|
||||||
on_d: EventHandler[no_args_event_spec]
|
on_d: EventHandler[no_args_event_spec]
|
||||||
on_e: EventHandler
|
on_e: EventHandler
|
||||||
on_f: EventHandler[lambda a, b, c: [c, b, a]]
|
|
||||||
|
|
||||||
custom_component = ReferenceComponent.create()
|
custom_component = ReferenceComponent.create()
|
||||||
test_component = TestComponent.create()
|
test_component = TestComponent.create()
|
||||||
|
@ -199,16 +199,15 @@ def test_event_redirect(input, output):
|
|||||||
input: The input for running the test.
|
input: The input for running the test.
|
||||||
output: The expected output to validate the test.
|
output: The expected output to validate the test.
|
||||||
"""
|
"""
|
||||||
path, external, replace = input
|
path, is_external, replace = input
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if external is not None:
|
if is_external is not None:
|
||||||
kwargs["external"] = external
|
kwargs["is_external"] = is_external
|
||||||
if replace is not None:
|
if replace is not None:
|
||||||
kwargs["replace"] = replace
|
kwargs["replace"] = replace
|
||||||
spec = event.redirect(path, **kwargs)
|
spec = event.redirect(path, **kwargs)
|
||||||
assert isinstance(spec, EventSpec)
|
assert isinstance(spec, EventSpec)
|
||||||
assert spec.handler.fn.__qualname__ == "_redirect"
|
assert spec.handler.fn.__qualname__ == "_redirect"
|
||||||
|
|
||||||
assert format.format_event(spec) == output
|
assert format.format_event(spec) == output
|
||||||
|
|
||||||
|
|
||||||
|
@ -1144,7 +1144,7 @@ def test_child_state():
|
|||||||
|
|
||||||
class ChildState(MainState):
|
class ChildState(MainState):
|
||||||
@computed_var
|
@computed_var
|
||||||
def rendered_var(self):
|
def rendered_var(self) -> int:
|
||||||
return self.v
|
return self.v
|
||||||
|
|
||||||
ms = MainState()
|
ms = MainState()
|
||||||
@ -1421,7 +1421,7 @@ def test_computed_var_dependencies():
|
|||||||
return self.testprop
|
return self.testprop
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def comp_w(self):
|
def comp_w(self) -> Callable[[], int]:
|
||||||
"""Nested lambda.
|
"""Nested lambda.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -1430,7 +1430,7 @@ def test_computed_var_dependencies():
|
|||||||
return lambda: self.w
|
return lambda: self.w
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def comp_x(self):
|
def comp_x(self) -> Callable[[], int]:
|
||||||
"""Nested function.
|
"""Nested function.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -1443,7 +1443,7 @@ def test_computed_var_dependencies():
|
|||||||
return _
|
return _
|
||||||
|
|
||||||
@rx.var
|
@rx.var
|
||||||
def comp_y(self) -> List[int]:
|
def comp_y(self) -> list[int]:
|
||||||
"""Comprehension iterating over attribute.
|
"""Comprehension iterating over attribute.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
@ -3128,7 +3128,7 @@ async def test_get_state_from_sibling_not_cached(mock_app: rx.App, token: str):
|
|||||||
child3_var: int = 0
|
child3_var: int = 0
|
||||||
|
|
||||||
@rx.var(cache=False)
|
@rx.var(cache=False)
|
||||||
def v(self):
|
def v(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class Grandchild3(Child3):
|
class Grandchild3(Child3):
|
||||||
|
@ -11,7 +11,10 @@ import reflex as rx
|
|||||||
from reflex.base import Base
|
from reflex.base import Base
|
||||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||||
from reflex.state import BaseState
|
from reflex.state import BaseState
|
||||||
from reflex.utils.exceptions import PrimitiveUnserializableToJSON
|
from reflex.utils.exceptions import (
|
||||||
|
PrimitiveUnserializableToJSON,
|
||||||
|
UntypedComputedVarError,
|
||||||
|
)
|
||||||
from reflex.utils.imports import ImportVar
|
from reflex.utils.imports import ImportVar
|
||||||
from reflex.vars import VarData
|
from reflex.vars import VarData
|
||||||
from reflex.vars.base import (
|
from reflex.vars.base import (
|
||||||
@ -804,7 +807,7 @@ def test_shadow_computed_var_error(request: pytest.FixtureRequest, fixture: str)
|
|||||||
request: Fixture Request.
|
request: Fixture Request.
|
||||||
fixture: The state fixture.
|
fixture: The state fixture.
|
||||||
"""
|
"""
|
||||||
with pytest.raises(NameError):
|
with pytest.raises(UntypedComputedVarError):
|
||||||
state = request.getfixturevalue(fixture)
|
state = request.getfixturevalue(fixture)
|
||||||
state.var_without_annotation.foo
|
state.var_without_annotation.foo
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user