瀏覽代碼

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>
Thomas Brandého 3 月之前
父節點
當前提交
4c2b2ed1c6
共有 52 個文件被更改,包括 703 次插入1044 次删除
  1. 4 10
      .github/workflows/benchmarks.yml
  2. 1 1
      .github/workflows/check_outdated_dependencies.yml
  3. 3 8
      .github/workflows/integration_tests.yml
  4. 3 7
      .github/workflows/unit_tests.yml
  5. 1 1
      .pre-commit-config.yaml
  6. 2 2
      CONTRIBUTING.md
  7. 1 1
      README.md
  8. 1 1
      docs/de/README.md
  9. 1 1
      docs/es/README.md
  10. 1 1
      docs/in/README.md
  11. 1 1
      docs/it/README.md
  12. 1 1
      docs/ja/README.md
  13. 1 1
      docs/kr/README.md
  14. 1 1
      docs/pe/README.md
  15. 1 1
      docs/pt/pt_br/README.md
  16. 1 1
      docs/tr/README.md
  17. 1 1
      docs/vi/README.md
  18. 1 1
      docs/zh/zh_cn/README.md
  19. 1 1
      docs/zh/zh_tw/README.md
  20. 399 467
      poetry.lock
  21. 2 3
      pyproject.toml
  22. 1 1
      reflex/.templates/jinja/custom_components/pyproject.toml.jinja2
  23. 0 16
      reflex/__init__.py
  24. 0 1
      reflex/__init__.pyi
  25. 1 39
      reflex/components/component.py
  26. 3 12
      reflex/components/datadisplay/code.py
  27. 2 2
      reflex/components/el/elements/forms.py
  28. 4 4
      reflex/components/el/elements/forms.pyi
  29. 6 6
      reflex/components/radix/primitives/form.pyi
  30. 64 114
      reflex/event.py
  31. 0 2
      reflex/experimental/__init__.py
  32. 0 37
      reflex/experimental/assets.py
  33. 19 6
      reflex/reflex.py
  34. 4 7
      reflex/state.py
  35. 3 1
      reflex/style.py
  36. 3 11
      reflex/utils/console.py
  37. 42 20
      reflex/utils/exceptions.py
  38. 12 54
      reflex/utils/exec.py
  39. 0 32
      reflex/utils/format.py
  40. 7 17
      reflex/utils/prerequisites.py
  41. 1 1
      reflex/utils/telemetry.py
  42. 7 44
      reflex/vars/base.py
  43. 1 1
      tests/integration/init-test/Dockerfile
  44. 25 23
      tests/integration/test_call_script.py
  45. 6 6
      tests/integration/test_client_storage.py
  46. 0 13
      tests/units/assets/test_assets.py
  47. 6 8
      tests/units/components/datadisplay/conftest.py
  48. 5 4
      tests/units/components/datadisplay/test_datatable.py
  49. 40 39
      tests/units/components/test_component.py
  50. 3 4
      tests/units/test_event.py
  51. 5 5
      tests/units/test_state.py
  52. 5 2
      tests/units/test_var.py

+ 4 - 10
.github/workflows/benchmarks.yml

@@ -81,24 +81,18 @@ jobs:
       matrix:
         # Show OS combos first in GUI
         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:
           - os: windows-latest
-            python-version: "3.10.16"
-          - os: windows-latest
-            python-version: "3.9.21"
+            python-version: '3.10.16'
           # keep only one python version for MacOS
           - os: macos-latest
-            python-version: "3.9.21"
-          - os: macos-latest
-            python-version: "3.10.16"
+            python-version: '3.10.16'
           - os: macos-latest
             python-version: "3.11.11"
         include:
           - os: windows-latest
-            python-version: "3.10.11"
-          - os: windows-latest
-            python-version: "3.9.13"
+            python-version: '3.10.11'
 
     runs-on: ${{ matrix.os }}
     steps:

+ 1 - 1
.github/workflows/check_outdated_dependencies.yml

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

+ 3 - 8
.github/workflows/integration_tests.yml

@@ -43,22 +43,17 @@ jobs:
       matrix:
         # Show OS combos first in GUI
         os: [ubuntu-latest, windows-latest]
-        python-version: ["3.9.21", "3.10.16", "3.11.11", "3.12.8", "3.13.1"]
-        # Windows is a bit behind on Python version availability in Github
+        python-version: ['3.10.16', '3.11.11', '3.12.8', '3.13.1']
         exclude:
           - os: windows-latest
             python-version: "3.11.11"
           - os: windows-latest
-            python-version: "3.10.16"
-          - os: windows-latest
-            python-version: "3.9.21"
+            python-version: '3.10.16'
         include:
           - os: windows-latest
             python-version: "3.11.9"
           - os: windows-latest
-            python-version: "3.10.11"
-          - os: windows-latest
-            python-version: "3.9.13"
+            python-version: '3.10.11'
 
     runs-on: ${{ matrix.os }}
     steps:

+ 3 - 7
.github/workflows/unit_tests.yml

@@ -28,22 +28,18 @@ jobs:
       fail-fast: false
       matrix:
         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:
           - os: windows-latest
             python-version: "3.11.11"
           - os: windows-latest
             python-version: "3.10.16"
-          - os: windows-latest
-            python-version: "3.9.21"
         include:
           - os: windows-latest
             python-version: "3.11.9"
           - os: windows-latest
             python-version: "3.10.11"
-          - os: windows-latest
-            python-version: "3.9.13"
     runs-on: ${{ matrix.os }}
 
     # Service containers to run with `runner-job`
@@ -92,8 +88,8 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        # Note: py39, 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"]
+        # Note: py310, py311 versions chosen due to available arm64 darwin builds.
+        python-version: ["3.10.11", "3.11.9", "3.12.8", "3.13.1"]
     runs-on: macos-latest
     steps:
       - uses: actions/checkout@v4

+ 1 - 1
.pre-commit-config.yaml

@@ -28,7 +28,7 @@ repos:
         entry: python3 scripts/make_pyi.py
 
   - repo: https://github.com/RobertCraigie/pyright-python
-    rev: v1.1.313
+    rev: v1.1.334
     hooks:
       - id: pyright
         args: [reflex, tests]

+ 2 - 2
CONTRIBUTING.md

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

+ 1 - 1
README.md

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

+ 1 - 1
docs/de/README.md

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

+ 1 - 1
docs/es/README.md

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

+ 1 - 1
docs/in/README.md

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

+ 1 - 1
docs/it/README.md

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

+ 1 - 1
docs/ja/README.md

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

+ 1 - 1
docs/kr/README.md

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

+ 1 - 1
docs/pe/README.md

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

+ 1 - 1
docs/pt/pt_br/README.md

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

+ 1 - 1
docs/tr/README.md

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

+ 1 - 1
docs/vi/README.md

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

+ 1 - 1
docs/zh/zh_cn/README.md

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

+ 1 - 1
docs/zh/zh_tw/README.md

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

文件差異過大導致無法顯示
+ 399 - 467
poetry.lock


+ 2 - 3
pyproject.toml

@@ -18,7 +18,7 @@ keywords = ["web", "framework"]
 classifiers = ["Development Status :: 4 - Beta"]
 
 [tool.poetry.dependencies]
-python = "^3.9"
+python = "^3.10"
 fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
 gunicorn = ">=20.1.0,<24.0"
 jinja2 = ">=3.1.2,<4.0"
@@ -50,7 +50,6 @@ httpx = ">=0.25.1,<1.0"
 twine = ">=4.0.0,<7.0"
 tomlkit = ">=0.12.4,<1.0"
 lazy_loader = ">=0.4"
-reflex-chakra = ">=0.6.0"
 typing_extensions = ">=4.6.0"
 
 [tool.poetry.group.dev.dependencies]
@@ -102,5 +101,5 @@ asyncio_default_fixture_loop_scope = "function"
 asyncio_mode = "auto"
 
 [tool.codespell]
-skip = "docs/*,*.html,examples/*, *.pyi"
+skip = "docs/*,*.html,examples/*, *.pyi, poetry.lock"
 ignore-words-list = "te, TreeE"

+ 1 - 1
reflex/.templates/jinja/custom_components/pyproject.toml.jinja2

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

+ 0 - 16
reflex/__init__.py

@@ -303,7 +303,6 @@ _MAPPING: dict = {
     "event": [
         "EventChain",
         "EventHandler",
-        "background",
         "call_script",
         "call_function",
         "run_script",
@@ -367,19 +366,4 @@ getattr, __dir__, __all__ = lazy_loader.attach(
 
 
 def __getattr__(name):
-    if name == "chakra":
-        from reflex.utils import console
-
-        console.deprecate(
-            "rx.chakra",
-            reason="and moved to a separate package. "
-            "To continue using Chakra UI components, install the `reflex-chakra` package via `pip install "
-            "reflex-chakra`.",
-            deprecation_version="0.6.0",
-            removal_version="0.7.0",
-            dedupe=True,
-        )
-        import reflex_chakra as rc
-
-        return rc
     return getattr(name)

+ 0 - 1
reflex/__init__.pyi

@@ -156,7 +156,6 @@ from .constants import Env as Env
 from .constants.colors import Color as Color
 from .event import EventChain as EventChain
 from .event import EventHandler as EventHandler
-from .event import background as background
 from .event import call_function as call_function
 from .event import call_script as call_script
 from .event import clear_local_storage as clear_local_storage

+ 1 - 39
reflex/components/component.py

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

+ 3 - 12
reflex/components/datadisplay/code.py

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

+ 2 - 2
reflex/components/el/elements/forms.py

@@ -102,7 +102,7 @@ class Fieldset(Element):
     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.
 
     Returns:
@@ -111,7 +111,7 @@ def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]:
     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.
 
     Returns:

+ 4 - 4
reflex/components/el/elements/forms.pyi

@@ -270,8 +270,8 @@ class Fieldset(Element):
         """
         ...
 
-def on_submit_event_spec() -> Tuple[Var[Dict[str, Any]]]: ...
-def on_submit_string_event_spec() -> Tuple[Var[Dict[str, str]]]: ...
+def on_submit_event_spec() -> Tuple[Var[dict[str, Any]]]: ...
+def on_submit_string_event_spec() -> Tuple[Var[dict[str, str]]]: ...
 
 class Form(BaseHTML):
     @overload
@@ -341,10 +341,10 @@ class Form(BaseHTML):
         on_submit: Optional[
             Union[
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
                 ],
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
                 ],
             ]
         ] = None,

+ 6 - 6
reflex/components/radix/primitives/form.pyi

@@ -132,10 +132,10 @@ class FormRoot(FormComponent, HTMLForm):
         on_submit: Optional[
             Union[
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
                 ],
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
                 ],
             ]
         ] = None,
@@ -608,10 +608,10 @@ class Form(FormRoot):
         on_submit: Optional[
             Union[
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
                 ],
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
                 ],
             ]
         ] = None,
@@ -741,10 +741,10 @@ class FormNamespace(ComponentNamespace):
         on_submit: Optional[
             Union[
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, Any]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, Any]], BASE_STATE]
                 ],
                 Union[
-                    EventType[[], BASE_STATE], EventType[[Dict[str, str]], BASE_STATE]
+                    EventType[[], BASE_STATE], EventType[[dict[str, str]], BASE_STATE]
                 ],
             ]
         ] = None,

+ 64 - 114
reflex/event.py

@@ -25,7 +25,6 @@ from typing import (
     overload,
 )
 
-import typing_extensions
 from typing_extensions import (
     Concatenate,
     ParamSpec,
@@ -40,7 +39,11 @@ from typing_extensions import (
 from reflex import constants
 from reflex.constants.state import FRONTEND_EVENT_STATE
 from reflex.utils import console, format
-from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgTypeMismatch
+from reflex.utils.exceptions import (
+    EventFnArgMismatchError,
+    EventHandlerArgTypeMismatchError,
+    MissingAnnotationError,
+)
 from reflex.utils.types import ArgsSpec, GenericType, typehint_issubclass
 from reflex.vars import VarData
 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"
 
 
-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(
     init=True,
     frozen=True,
@@ -813,29 +790,10 @@ def server_side(name: str, sig: inspect.Signature, **kwargs) -> EventSpec:
     )
 
 
-@overload
-def redirect(
-    path: str | Var[str],
-    is_external: Optional[bool] = None,
-    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,
+    is_external: bool = False,
     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:
     """Redirect to a new path.
 
@@ -843,26 +801,10 @@ def redirect(
         path: The path to redirect to.
         is_external: Whether to open in new tab or not.
         replace: If True, the current page will not create a new history entry.
-        external(Deprecated): Whether to open in new tab or not.
 
     Returns:
         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(
         "_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.
         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:
         The event spec from calling the event handler.
 
-    # noqa: DAR401 failure
-
+    #noqa: DAR401
     """
     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] = []
 
-    if event_spec_return_types:
-        failures = []
+    try:
+        type_hints_of_provided_callback = get_type_hints(event_callback.fn)
+    except NameError:
+        type_hints_of_provided_callback = {}
 
+    if event_spec_return_types:
         event_callback_spec = inspect.getfullargspec(event_callback.fn)
 
         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
             ]
 
-            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
             for i, arg in enumerate(event_callback_spec.args[1:]):
                 if arg not in type_hints_of_provided_callback:
                     continue
 
+                type_match_found.setdefault(arg, False)
+
                 try:
                     compare_result = typehint_issubclass(
                         args_types_without_vars[i], type_hints_of_provided_callback[arg]
                     )
-                except TypeError:
-                    # TODO: In 0.7.0, remove this block and raise the exception
-                    # raise TypeError(
-                    #     f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_handler.fn.__qualname__} provided for {key}." # noqa: ERA001
-                    # ) from e
-                    console.warn(
+                except TypeError as te:
+                    raise TypeError(
                         f"Could not compare types {args_types_without_vars[i]} and {type_hints_of_provided_callback[arg]} for argument {arg} of {event_callback.fn.__qualname__} provided for {key}."
-                    )
-                    compare_result = False
+                    ) from te
 
                 if compare_result:
+                    type_match_found[arg] = True
                     continue
                 else:
-                    failure = EventHandlerArgTypeMismatch(
-                        f"Event handler {key} expects {args_types_without_vars[i]} for argument {arg} but got {type_hints_of_provided_callback[arg]} as annotated in {event_callback.fn.__qualname__} instead."
+                    type_match_found[arg] = False
+                    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:
                     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"This may lead to unexpected behavior but is intentionally ignored for {key}."
                     )
-                return event_callback(*event_spec_args)
-
-        if failures:
-            console.deprecate(
-                "Mismatched event handler argument types",
-                "\n".join([str(f) for f in failures]),
-                "0.6.5",
-                "0.7.0",
-            )
+                break
+
+    if delayed_exceptions:
+        raise delayed_exceptions[0]
 
     return event_callback(*event_spec_args)  # type: ignore
 
@@ -1420,26 +1357,26 @@ def unwrap_var_annotation(annotation: GenericType):
     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.
 
     Args:
         annotations: The annotations.
         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:
         The resolved annotation.
     """
     annotation = annotations.get(arg_name)
     if annotation is None:
-        console.deprecate(
-            feature_name="Unannotated event handler arguments",
-            reason="Provide type annotations for event handler arguments.",
-            deprecation_version="0.6.3",
-            removal_version="0.7.0",
-        )
-        # Allow arbitrary attribute access two levels deep until removed.
-        return Dict[str, dict]
+        if not isinstance(spec, types.LambdaType):
+            raise MissingAnnotationError(var_name=arg_name)
+        else:
+            return dict[str, dict]
     return annotation
 
 
@@ -1461,7 +1398,13 @@ def parse_args_spec(arg_spec: ArgsSpec | Sequence[ArgsSpec]):
         arg_spec(
             *[
                 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
             ]
@@ -1477,7 +1420,7 @@ def check_fn_match_arg_spec(
     func_name: str | None = None,
 ):
     """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:
         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.
 
     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
     # 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)
 
     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"{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"
@@ -1812,8 +1755,6 @@ V3 = TypeVar("V3")
 V4 = TypeVar("V4")
 V5 = TypeVar("V5")
 
-background_event_decorator = background
-
 
 class EventCallback(Generic[P, T]):
     """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.
             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:
             The wrapped function.
         """
@@ -1994,7 +1938,13 @@ class EventNamespace(types.SimpleNamespace):
             func: Callable[Concatenate[BASE_STATE, P], T],
         ) -> EventCallback[P, T]:
             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
 
         if func is not None:

+ 0 - 2
reflex/experimental/__init__.py

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

+ 0 - 37
reflex/experimental/assets.py

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

+ 19 - 6
reflex/reflex.py

@@ -505,6 +505,7 @@ def deploy(
     ),
 ):
     """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.v2 import cli as hosting_cli
 
@@ -516,6 +517,21 @@ def deploy(
     # Set the log level.
     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.
     # There is user interaction for requirements update.
     if interactive:
@@ -525,9 +541,7 @@ def deploy(
     if prerequisites.needs_reinit(frontend=True):
         _init(name=config.app_name, loglevel=loglevel)
     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(
         app_name=app_name,
         app_id=app_id,
@@ -551,12 +565,11 @@ def deploy(
         envfile=envfile,
         hostname=hostname,
         interactive=interactive,
-        loglevel=type(loglevel).INFO,  # type: ignore
+        loglevel=convert_reflex_loglevel_to_reflex_cli_loglevel(loglevel),
         token=token,
         project=project,
-        config_path=config_path,
         project_name=project_name,
-        **extra,
+        **({"config_path": config_path} if config_path is not None else {}),
     )
 
 

+ 4 - 7
reflex/state.py

@@ -1341,12 +1341,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
             if field.allow_none and not is_optional(field_type):
                 field_type = Union[field_type, None]
             if not _isinstance(value, field_type):
-                console.deprecate(
-                    "mismatched-type-assignment",
-                    f"Tried to assign value {value} of type {type(value)} to field {type(self).__name__}.{name} of type {field_type}."
-                    " This might lead to unexpected behavior.",
-                    "0.6.5",
-                    "0.7.0",
+                console.error(
+                    f"Expected field '{type(self).__name__}.{name}' to receive type '{field_type}',"
+                    f" but got '{value}' of type '{type(value)}'."
                 )
 
         # Set the attribute.
@@ -1831,7 +1828,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
             if (
                 isinstance(value, dict)
                 and inspect.isclass(hinted_args)
-                and not types.is_generic_alias(hinted_args)  # py3.9-py3.10
+                and not types.is_generic_alias(hinted_args)  # py3.10
             ):
                 if issubclass(hinted_args, Model):
                     # Remove non-fields from the payload

+ 3 - 1
reflex/style.py

@@ -287,7 +287,9 @@ class Style(dict):
         _var = LiteralVar.create(value)
         if _var is not None:
             # 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)
 
 

+ 3 - 11
reflex/utils/console.py

@@ -51,20 +51,12 @@ def set_log_level(log_level: LogLevel):
         log_level: The log level to set.
 
     Raises:
-        ValueError: If the log level is invalid.
+        TypeError: If the log level is a string.
     """
     if not isinstance(log_level, LogLevel):
-        deprecate(
-            feature_name="Passing a string to set_log_level",
-            reason="use reflex.constants.LogLevel enum instead",
-            deprecation_version="0.6.6",
-            removal_version="0.7.0",
+        raise TypeError(
+            f"log_level must be a LogLevel enum value, got {log_level} of type {type(log_level)} instead."
         )
-        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
     _LOG_LEVEL = log_level
 

+ 42 - 20
reflex/utils/exceptions.py

@@ -1,6 +1,6 @@
 """Custom Exceptions."""
 
-from typing import Any, NoReturn
+from typing import Any
 
 
 class ReflexError(Exception):
@@ -75,6 +75,30 @@ class VarAttributeError(ReflexError, AttributeError):
     """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):
     """Custom ValueError for upload related errors."""
 
@@ -111,11 +135,11 @@ class MatchTypeError(ReflexError, TypeError):
     """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."""
 
 
-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."""
 
 
@@ -186,27 +210,25 @@ class StateMismatchError(ReflexError, ValueError):
 class SystemPackageMissingError(ReflexError):
     """Raised when a system package is missing."""
 
+    def __init__(self, package: str):
+        """Initialize the SystemPackageMissingError.
 
-class EventDeserializationError(ReflexError, ValueError):
-    """Raised when an event cannot be deserialized."""
-
-
-def raise_system_package_missing_error(package: str) -> NoReturn:
-    """Raise a SystemPackageMissingError.
+        Args:
+            package: The missing package.
+        """
+        from reflex.constants import IS_MACOS
 
-    Args:
-        package: The name of the missing system package.
+        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}"
+        )
 
-    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 EventDeserializationError(ReflexError, ValueError):
+    """Raised when an event cannot be deserialized."""
 
 
 class InvalidLockWarningThresholdError(ReflexError):

+ 12 - 54
reflex/utils/exec.py

@@ -240,25 +240,28 @@ def run_backend(
         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.
 
     Returns:
         The reload directories for the backend.
     """
     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__:
         module_path = Path(config.app_module.__file__).resolve().parent
+
         while module_path.parent.name:
-            for parent_file in module_path.parent.iterdir():
-                if parent_file == "__init__.py":
-                    # go up a level to find dir without `__init__.py`
-                    module_path = module_path.parent
-                    break
+            if any(
+                sibling_file.name == "__init__.py"
+                for sibling_file in module_path.parent.iterdir()
+            ):
+                # go up a level to find dir without `__init__.py`
+                module_path = module_path.parent
             else:
                 break
-        reload_dirs.append(str(module_path))
+
+        reload_dirs = [module_path]
     return reload_dirs
 
 
@@ -278,7 +281,7 @@ def run_uvicorn_backend(host, port, loglevel: LogLevel):
         port=port,
         log_level=loglevel.value,
         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()
     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()

+ 0 - 32
reflex/utils/format.py

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

+ 7 - 17
reflex/utils/prerequisites.py

@@ -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.exceptions import (
     GeneratedCodeHasNoFunctionDefs,
-    raise_system_package_missing_error,
+    SystemPackageMissingError,
 )
 from reflex.utils.format import format_library_name
 from reflex.utils.registry import _get_npm_registry
@@ -86,18 +86,6 @@ def get_web_dir() -> Path:
     return environment.REFLEX_WEB_WORKDIR.get()
 
 
-def _python_version_check():
-    """Emit deprecation warning for deprecated python versions."""
-    # Check for end-of-life python versions.
-    if sys.version_info < (3, 10):
-        console.deprecate(
-            feature_name="Support for Python 3.9 and older",
-            reason="please upgrade to Python 3.10 or newer",
-            deprecation_version="0.6.0",
-            removal_version="0.7.0",
-        )
-
-
 def check_latest_package_version(package_name: str):
     """Check if the latest version of the package is installed.
 
@@ -120,8 +108,6 @@ def check_latest_package_version(package_name: str):
             console.warn(
                 f"Your version ({current_version}) of {package_name} is out of date. Upgrade to {latest_version} with 'pip install {package_name} --upgrade'"
             )
-        # Check for deprecated python versions
-        _python_version_check()
     except Exception:
         pass
 
@@ -885,7 +871,11 @@ def install_node():
 
 
 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()
     one_drive_in_path = windows_check_onedrive_in_path()
     if constants.IS_WINDOWS and (not win_supported or one_drive_in_path):
@@ -924,7 +914,7 @@ def install_bun():
     else:
         unzip_path = path_ops.which("unzip")
         if unzip_path is None:
-            raise_system_package_missing_error("unzip")
+            raise SystemPackageMissingError("unzip")
 
         # Run the bun install script.
         download_and_run(

+ 1 - 1
reflex/utils/telemetry.py

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

+ 7 - 44
reflex/vars/base.py

@@ -46,6 +46,7 @@ from reflex.base import Base
 from reflex.constants.compiler import Hooks
 from reflex.utils import console, exceptions, imports, serializers, types
 from reflex.utils.exceptions import (
+    UntypedComputedVarError,
     VarAttributeError,
     VarDependencyError,
     VarTypeError,
@@ -545,52 +546,21 @@ class Var(Generic[VAR_TYPE]):
     def create(
         cls,
         value: Any,
-        _var_is_local: bool | None = None,
-        _var_is_string: bool | None = None,
         _var_data: VarData | None = None,
     ) -> Var:
         """Create a var from a value.
 
         Args:
             value: The value to create the var from.
-            _var_is_local: Whether the var is local. Deprecated.
-            _var_is_string: Whether the var is a string literal. Deprecated.
             _var_data: Additional hooks and imports associated with the Var.
 
         Returns:
             The var.
         """
-        if _var_is_local is not None:
-            console.deprecate(
-                feature_name="_var_is_local",
-                reason="The _var_is_local argument is not supported for Var. "
-                "If you want to create a Var from a raw Javascript expression, use the constructor directly",
-                deprecation_version="0.6.0",
-                removal_version="0.7.0",
-            )
-        if _var_is_string is not None:
-            console.deprecate(
-                feature_name="_var_is_string",
-                reason="The _var_is_string argument is not supported for Var. "
-                "If you want to create a Var from a raw Javascript expression, use the constructor directly",
-                deprecation_version="0.6.0",
-                removal_version="0.7.0",
-            )
-
         # If the value is already a var, do nothing.
         if isinstance(value, Var):
             return value
 
-        # 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)
 
     @classmethod
@@ -1863,19 +1833,14 @@ class ComputedVar(Var[RETURN_TYPE]):
 
         Raises:
             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(
             "return", Any
         )
 
         if hint is Any:
-            console.deprecate(
-                "untyped-computed-var",
-                "ComputedVar should have a return type annotation.",
-                "0.6.5",
-                "0.7.0",
-            )
-
+            raise UntypedComputedVarError(var_name=fget.__name__)
         kwargs.setdefault("_js_expr", fget.__name__)
         kwargs.setdefault("_var_type", hint)
 
@@ -1948,6 +1913,7 @@ class ComputedVar(Var[RETURN_TYPE]):
             "_var_data": kwargs.pop(
                 "_var_data", VarData.merge(self._var_data, merge_var_data)
             ),
+            "return_type": kwargs.pop("return_type", self._var_type),
         }
 
         if kwargs:
@@ -2082,12 +2048,9 @@ class ComputedVar(Var[RETURN_TYPE]):
             value = getattr(instance, self._cache_attr)
 
         if not _isinstance(value, self._var_type):
-            console.deprecate(
-                "mismatched-computed-var-return",
-                f"Computed var {type(instance).__name__}.{self._js_expr} returned value of type {type(value)}, "
-                f"expected {self._var_type}. This might cause unexpected behavior.",
-                "0.6.5",
-                "0.7.0",
+            console.error(
+                f"Computed var '{type(instance).__name__}.{self._js_expr}' must return"
+                f" type '{self._var_type}', got '{type(value)}'."
             )
 
         return value

+ 1 - 1
tests/integration/init-test/Dockerfile

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

+ 25 - 23
tests/integration/test_call_script.py

@@ -16,7 +16,7 @@ from .utils import SessionStorage
 def CallScript():
     """A test app for browser javascript integration."""
     from pathlib import Path
-    from typing import Dict, List, Optional, Union
+    from typing import Optional, Union
 
     import reflex as rx
 
@@ -43,15 +43,17 @@ def CallScript():
     external_scripts = inline_scripts.replace("inline", "external")
 
     class CallScriptState(rx.State):
-        results: List[Optional[Union[str, Dict, List]]] = []
-        inline_counter: int = 0
-        external_counter: int = 0
+        results: rx.Field[list[Optional[Union[str, dict, list]]]] = rx.field([])
+        inline_counter: rx.Field[int] = rx.field(0)
+        external_counter: rx.Field[int] = rx.field(0)
         value: str = "Initial"
-        last_result: str = ""
+        last_result: int = 0
 
+        @rx.event
         def call_script_callback(self, result):
             self.results.append(result)
 
+        @rx.event
         def call_script_callback_other_arg(self, result, other_arg):
             self.results.append([other_arg, result])
 
@@ -91,7 +93,7 @@ def CallScript():
         def call_script_inline_return_lambda(self):
             return rx.call_script(
                 "inline2()",
-                callback=lambda result: CallScriptState.call_script_callback_other_arg(  # type: ignore
+                callback=lambda result: CallScriptState.call_script_callback_other_arg(
                     result, "lambda"
                 ),
             )
@@ -100,7 +102,7 @@ def CallScript():
         def get_inline_counter(self):
             return rx.call_script(
                 "inline_counter",
-                callback=CallScriptState.set_inline_counter,  # type: ignore
+                callback=CallScriptState.setvar("inline_counter"),
             )
 
         @rx.event
@@ -139,7 +141,7 @@ def CallScript():
         def call_script_external_return_lambda(self):
             return rx.call_script(
                 "external2()",
-                callback=lambda result: CallScriptState.call_script_callback_other_arg(  # type: ignore
+                callback=lambda result: CallScriptState.call_script_callback_other_arg(
                     result, "lambda"
                 ),
             )
@@ -148,28 +150,28 @@ def CallScript():
         def get_external_counter(self):
             return rx.call_script(
                 "external_counter",
-                callback=CallScriptState.set_external_counter,  # type: ignore
+                callback=CallScriptState.setvar("external_counter"),
             )
 
         @rx.event
         def call_with_var_f_string(self):
             return rx.call_script(
                 f"{rx.Var('inline_counter')} + {rx.Var('external_counter')}",
-                callback=CallScriptState.set_last_result,  # type: ignore
+                callback=CallScriptState.setvar("last_result"),
             )
 
         @rx.event
         def call_with_var_str_cast(self):
             return rx.call_script(
                 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
         def call_with_var_f_string_wrapped(self):
             return rx.call_script(
                 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
@@ -178,7 +180,7 @@ def CallScript():
                 rx.Var(
                     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
@@ -193,17 +195,17 @@ def CallScript():
     def index():
         return rx.vstack(
             rx.input(
-                value=CallScriptState.inline_counter.to(str),  # type: ignore
+                value=CallScriptState.inline_counter.to(str),
                 id="inline_counter",
                 read_only=True,
             ),
             rx.input(
-                value=CallScriptState.external_counter.to(str),  # type: ignore
+                value=CallScriptState.external_counter.to(str),
                 id="external_counter",
                 read_only=True,
             ),
             rx.text_area(
-                value=CallScriptState.results.to_string(),  # type: ignore
+                value=CallScriptState.results.to_string(),
                 id="results",
                 read_only=True,
             ),
@@ -273,7 +275,7 @@ def CallScript():
                 CallScriptState.value,
                 on_click=rx.call_script(
                     "'updated'",
-                    callback=CallScriptState.set_value,  # type: ignore
+                    callback=CallScriptState.setvar("value"),
                 ),
                 id="update_value",
             ),
@@ -282,7 +284,7 @@ def CallScript():
                 value=CallScriptState.last_result,
                 id="last_result",
                 read_only=True,
-                on_click=CallScriptState.set_last_result(""),  # type: ignore
+                on_click=CallScriptState.setvar("last_result", 0),
             ),
             rx.button(
                 "call_with_var_f_string",
@@ -308,7 +310,7 @@ def CallScript():
                 "call_with_var_f_string_inline",
                 on_click=rx.call_script(
                     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",
             ),
@@ -316,7 +318,7 @@ def CallScript():
                 "call_with_var_str_cast_inline",
                 on_click=rx.call_script(
                     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",
             ),
@@ -326,7 +328,7 @@ def CallScript():
                     rx.Var(
                         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",
             ),
@@ -336,7 +338,7 @@ def CallScript():
                     rx.Var(
                         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",
             ),
@@ -483,7 +485,7 @@ def test_call_script_w_var(
     """
     assert_token(driver)
     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")
 

+ 6 - 6
tests/integration/test_client_storage.py

@@ -33,18 +33,18 @@ def ClientSide():
     class ClientSideSubState(ClientSideState):
         # cookies with default settings
         c1: str = rx.Cookie()
-        c2: rx.Cookie = "c2 default"  # type: ignore
+        c2: str = rx.Cookie("c2 default")
 
         # cookies with custom settings
         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/`
         c6: str = rx.Cookie(name="c6")
         c7: str = rx.Cookie("c7 default")
 
         # local storage with default settings
         l1: str = rx.LocalStorage()
-        l2: rx.LocalStorage = "l2 default"  # type: ignore
+        l2: str = rx.LocalStorage("l2 default")
 
         # local storage with custom settings
         l3: str = rx.LocalStorage(name="l3")
@@ -56,7 +56,7 @@ def ClientSide():
 
         # Session storage
         s1: str = rx.SessionStorage()
-        s2: rx.SessionStorage = "s2 default"  # type: ignore
+        s2: str = rx.SessionStorage("s2 default")
         s3: str = rx.SessionStorage(name="s3")
 
         def set_l6(self, my_param: str):
@@ -87,13 +87,13 @@ def ClientSide():
             rx.input(
                 placeholder="state var",
                 value=ClientSideState.state_var,
-                on_change=ClientSideState.set_state_var,  # type: ignore
+                on_change=ClientSideState.setvar("state_var"),
                 id="state_var",
             ),
             rx.input(
                 placeholder="input value",
                 value=ClientSideState.input_value,
-                on_change=ClientSideState.set_input_value,  # type: ignore
+                on_change=ClientSideState.setvar("input_value"),
                 id="input_value",
             ),
             rx.button(

+ 0 - 13
tests/units/assets/test_assets.py

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

+ 6 - 8
tests/units/components/datadisplay/conftest.py

@@ -1,7 +1,5 @@
 """Data display component tests fixtures."""
 
-from typing import List
-
 import pandas as pd
 import pytest
 
@@ -54,11 +52,11 @@ def data_table_state3():
     """
 
     class DataTableState(BaseState):
-        _data: List = []
-        _columns: List = ["col1", "col2"]
+        _data: list = []
+        _columns: list = ["col1", "col2"]
 
         @rx.var
-        def data(self) -> List:
+        def data(self) -> list:
             return self._data
 
         @rx.var
@@ -77,15 +75,15 @@ def data_table_state4():
     """
 
     class DataTableState(BaseState):
-        _data: List = []
-        _columns: List = ["col1", "col2"]
+        _data: list = []
+        _columns: list[str] = ["col1", "col2"]
 
         @rx.var
         def data(self):
             return self._data
 
         @rx.var
-        def columns(self) -> List:
+        def columns(self) -> list:
             return self._columns
 
     return DataTableState

+ 5 - 4
tests/units/components/datadisplay/test_datatable.py

@@ -4,6 +4,7 @@ import pytest
 import reflex as rx
 from reflex.components.gridjs.datatable import DataTable
 from reflex.utils import types
+from reflex.utils.exceptions import UntypedComputedVarError
 from reflex.utils.serializers import serialize, serialize_dataframe
 
 
@@ -75,17 +76,17 @@ def test_invalid_props(props):
     [
         (
             "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,
         ),
         (
             "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,
         ),
         (
             "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,
         ),
     ],
@@ -99,7 +100,7 @@ def test_computed_var_without_annotation(fixture, request, err_msg, is_data_fram
         err_msg: expected error message.
         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:
             DataTable.create(data=request.getfixturevalue(fixture).data)
         else:

+ 40 - 39
tests/units/components/test_component.py

@@ -19,6 +19,7 @@ from reflex.constants import EventTriggers
 from reflex.event import (
     EventChain,
     EventHandler,
+    JavascriptInputEvent,
     input_event,
     no_args_event_spec,
     parse_args_spec,
@@ -27,7 +28,11 @@ from reflex.event import (
 from reflex.state import BaseState
 from reflex.style import Style
 from reflex.utils import imports
-from reflex.utils.exceptions import ChildrenTypeError, EventFnArgMismatch
+from reflex.utils.exceptions import (
+    ChildrenTypeError,
+    EventFnArgMismatchError,
+    EventHandlerArgTypeMismatchError,
+)
 from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
 from reflex.vars import VarData
 from reflex.vars.base import LiteralVar, Var
@@ -94,11 +99,14 @@ def component2() -> Type[Component]:
         A test component.
     """
 
+    def on_prop_event_spec(e0: Any):
+        return [e0]
+
     class TestComponent2(Component):
         # A test list prop.
         arr: Var[List[str]]
 
-        on_prop_event: EventHandler[lambda e0: [e0]]
+        on_prop_event: EventHandler[on_prop_event_spec]
 
         def get_event_triggers(self) -> Dict[str, Any]:
             """Test controlled triggers.
@@ -818,10 +826,14 @@ def test_component_create_unpack_tuple_child(test_component, element, expected):
     assert fragment_wrapper.render() == expected
 
 
+class _Obj(Base):
+    custom: int = 0
+
+
 class C1State(BaseState):
     """State for testing C1 component."""
 
-    def mock_handler(self, _e, _bravo, _charlie):
+    def mock_handler(self, _e: JavascriptInputEvent, _bravo: dict, _charlie: _Obj):
         """Mock handler."""
         pass
 
@@ -829,10 +841,12 @@ class C1State(BaseState):
 def test_component_event_trigger_arbitrary_args():
     """Test that we can define arbitrary types for the args of an event trigger."""
 
-    class Obj(Base):
-        custom: int = 0
-
-    def on_foo_spec(_e, alpha: str, bravo: Dict[str, Any], charlie: Obj):
+    def on_foo_spec(
+        _e: Var[JavascriptInputEvent],
+        alpha: Var[str],
+        bravo: dict[str, Any],
+        charlie: Var[_Obj],
+    ):
         return [_e.target.value, bravo["nested"], charlie.custom + 42]
 
     class C1(Component):
@@ -845,13 +859,7 @@ def test_component_event_trigger_arbitrary_args():
                 "on_foo": on_foo_spec,
             }
 
-    comp = C1.create(on_foo=C1State.mock_handler)
-
-    assert comp.render()["props"][0] == (
-        "onFoo={((__e, _alpha, _bravo, _charlie) => (addEvents("
-        f'[(Event("{C1State.get_full_name()}.mock_handler", ({{ ["_e"] : __e["target"]["value"], ["_bravo"] : _bravo["nested"], ["_charlie"] : (_charlie["custom"] + 42) }}), ({{  }})))], '
-        "[__e, _alpha, _bravo, _charlie], ({  }))))}"
-    )
+    C1.create(on_foo=C1State.mock_handler)
 
 
 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.
     """
     # EventHandler args must match
-    with pytest.raises(EventFnArgMismatch):
+    with pytest.raises(EventFnArgMismatchError):
         component2.create(on_click=test_state.do_something_arg)
 
     # Multiple EventHandler args: all must match
-    with pytest.raises(EventFnArgMismatch):
+    with pytest.raises(EventFnArgMismatchError):
         component2.create(
             on_click=[test_state.do_something_arg, test_state.do_something]
         )
 
-    # Enable when 0.7.0 happens
     # # Event Handler types must match
-    # with pytest.raises(EventHandlerArgTypeMismatch):
-    #     component2.create(
-    #         on_user_visited_count_changed=test_state.do_something_with_bool # noqa: ERA001 RUF100
-    #     ) # noqa: ERA001 RUF100
-    # with pytest.raises(EventHandlerArgTypeMismatch):
-    #     component2.create(on_user_list_changed=test_state.do_something_with_int) #noqa: ERA001
-    # with pytest.raises(EventHandlerArgTypeMismatch):
-    #     component2.create(on_user_list_changed=test_state.do_something_with_list_int) #noqa: ERA001
-
-    # component2.create(on_open=test_state.do_something_with_int) #noqa: ERA001
-    # component2.create(on_open=test_state.do_something_with_bool) #noqa: ERA001
-    # component2.create(on_user_visited_count_changed=test_state.do_something_with_int) #noqa: ERA001
-    # component2.create(on_user_list_changed=test_state.do_something_with_list_str) #noqa: ERA001
+    with pytest.raises(EventHandlerArgTypeMismatchError):
+        component2.create(
+            on_user_visited_count_changed=test_state.do_something_with_bool
+        )
+    with pytest.raises(EventHandlerArgTypeMismatchError):
+        component2.create(on_user_list_changed=test_state.do_something_with_int)
+    with pytest.raises(EventHandlerArgTypeMismatchError):
+        component2.create(on_user_list_changed=test_state.do_something_with_list_int)
+
+    component2.create(on_open=test_state.do_something_with_int)
+    component2.create(on_open=test_state.do_something_with_bool)
+    component2.create(on_user_visited_count_changed=test_state.do_something_with_int)
+    component2.create(on_user_list_changed=test_state.do_something_with_list_str)
 
     # lambda cannot return weird values.
     with pytest.raises(ValueError):
@@ -944,15 +951,15 @@ def test_invalid_event_handler_args(component2, test_state):
         )
 
     # 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))
 
     # lambda returning EventHandler must match spec
-    with pytest.raises(EventFnArgMismatch):
+    with pytest.raises(EventFnArgMismatchError):
         component2.create(on_click=lambda: test_state.do_something_arg)
 
     # Mixed EventSpec and EventHandler must match spec.
-    with pytest.raises(EventFnArgMismatch):
+    with pytest.raises(EventFnArgMismatchError):
         component2.create(
             on_click=lambda: [
                 test_state.do_something_arg(1),
@@ -1801,21 +1808,15 @@ def test_custom_component_declare_event_handlers_in_fields():
             """
             return {
                 **super().get_event_triggers(),
-                "on_a": lambda e0: [e0],
                 "on_b": input_event,
-                "on_c": lambda e0: [],
                 "on_d": lambda: [],
                 "on_e": lambda: [],
-                "on_f": lambda a, b, c: [c, b, a],
             }
 
     class TestComponent(Component):
-        on_a: EventHandler[lambda e0: [e0]]
         on_b: EventHandler[input_event]
-        on_c: EventHandler[no_args_event_spec]
         on_d: EventHandler[no_args_event_spec]
         on_e: EventHandler
-        on_f: EventHandler[lambda a, b, c: [c, b, a]]
 
     custom_component = ReferenceComponent.create()
     test_component = TestComponent.create()

+ 3 - 4
tests/units/test_event.py

@@ -199,16 +199,15 @@ def test_event_redirect(input, output):
         input: The input for running the test.
         output: The expected output to validate the test.
     """
-    path, external, replace = input
+    path, is_external, replace = input
     kwargs = {}
-    if external is not None:
-        kwargs["external"] = external
+    if is_external is not None:
+        kwargs["is_external"] = is_external
     if replace is not None:
         kwargs["replace"] = replace
     spec = event.redirect(path, **kwargs)
     assert isinstance(spec, EventSpec)
     assert spec.handler.fn.__qualname__ == "_redirect"
-
     assert format.format_event(spec) == output
 
 

+ 5 - 5
tests/units/test_state.py

@@ -1144,7 +1144,7 @@ def test_child_state():
 
     class ChildState(MainState):
         @computed_var
-        def rendered_var(self):
+        def rendered_var(self) -> int:
             return self.v
 
     ms = MainState()
@@ -1421,7 +1421,7 @@ def test_computed_var_dependencies():
             return self.testprop
 
         @rx.var
-        def comp_w(self):
+        def comp_w(self) -> Callable[[], int]:
             """Nested lambda.
 
             Returns:
@@ -1430,7 +1430,7 @@ def test_computed_var_dependencies():
             return lambda: self.w
 
         @rx.var
-        def comp_x(self):
+        def comp_x(self) -> Callable[[], int]:
             """Nested function.
 
             Returns:
@@ -1443,7 +1443,7 @@ def test_computed_var_dependencies():
             return _
 
         @rx.var
-        def comp_y(self) -> List[int]:
+        def comp_y(self) -> list[int]:
             """Comprehension iterating over attribute.
 
             Returns:
@@ -3128,7 +3128,7 @@ async def test_get_state_from_sibling_not_cached(mock_app: rx.App, token: str):
         child3_var: int = 0
 
         @rx.var(cache=False)
-        def v(self):
+        def v(self) -> None:
             pass
 
     class Grandchild3(Child3):

+ 5 - 2
tests/units/test_var.py

@@ -11,7 +11,10 @@ import reflex as rx
 from reflex.base import Base
 from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
 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.vars import VarData
 from reflex.vars.base import (
@@ -804,7 +807,7 @@ def test_shadow_computed_var_error(request: pytest.FixtureRequest, fixture: str)
         request: Fixture Request.
         fixture: The state fixture.
     """
-    with pytest.raises(NameError):
+    with pytest.raises(UntypedComputedVarError):
         state = request.getfixturevalue(fixture)
         state.var_without_annotation.foo
 

部分文件因文件數量過多而無法顯示