Merge branch 'main' into add-validation-to-function-vars

This commit is contained in:
Khaleel Al-Adhami 2025-01-22 12:11:48 -08:00
commit 68b8c12127
77 changed files with 1068 additions and 1015 deletions

View File

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

View File

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

View File

@ -47,14 +47,14 @@ jobs:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
run-poetry-install: true run-poetry-install: true
create-venv-at-path: .venv create-venv-at-path: .venv
- run: poetry run uv pip install pyvirtualdisplay pillow pytest-split - run: poetry run uv pip install pyvirtualdisplay pillow pytest-split pytest-retry
- name: Run app harness tests - name: Run app harness tests
env: env:
SCREENSHOT_DIR: /tmp/screenshots/${{ matrix.state_manager }}/${{ matrix.python-version }}/${{ matrix.split_index }} SCREENSHOT_DIR: /tmp/screenshots/${{ matrix.state_manager }}/${{ matrix.python-version }}/${{ matrix.split_index }}
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
run: | run: |
poetry run playwright install chromium poetry run playwright install chromium
poetry run pytest tests/integration --splits 2 --group ${{matrix.split_index}} poetry run pytest tests/integration --retries 3 --maxfail=5 --splits 2 --group ${{matrix.split_index}}
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
name: Upload failed test screenshots name: Upload failed test screenshots
if: always() if: always()

View File

@ -33,7 +33,7 @@ env:
PR_TITLE: ${{ github.event.pull_request.title }} PR_TITLE: ${{ github.event.pull_request.title }}
jobs: jobs:
example-counter: example-counter-and-nba-proxy:
env: env:
OUTPUT_FILE: import_benchmark.json OUTPUT_FILE: import_benchmark.json
timeout-minutes: 30 timeout-minutes: 30
@ -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:
@ -119,6 +114,26 @@ jobs:
--benchmark-json "./reflex-examples/counter/${{ env.OUTPUT_FILE }}" --benchmark-json "./reflex-examples/counter/${{ env.OUTPUT_FILE }}"
--branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}"
--app-name "counter" --app-name "counter"
- name: Install requirements for nba proxy example
working-directory: ./reflex-examples/nba-proxy
run: |
poetry run uv pip install -r requirements.txt
- name: Install additional dependencies for DB access
run: poetry run uv pip install psycopg
- name: Check export --backend-only before init for nba-proxy example
working-directory: ./reflex-examples/nba-proxy
run: |
poetry run reflex export --backend-only
- name: Init Website for nba-proxy example
working-directory: ./reflex-examples/nba-proxy
run: |
poetry run reflex init --loglevel debug
- name: Run Website and Check for errors
run: |
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./reflex-examples/nba-proxy dev
reflex-web: reflex-web:
strategy: strategy:

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

497
poetry.lock generated
View File

@ -2,13 +2,13 @@
[[package]] [[package]]
name = "alembic" name = "alembic"
version = "1.14.0" version = "1.14.1"
description = "A database migration tool for SQLAlchemy." description = "A database migration tool for SQLAlchemy."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "alembic-1.14.0-py3-none-any.whl", hash = "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25"}, {file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"},
{file = "alembic-1.14.0.tar.gz", hash = "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b"}, {file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"},
] ]
[package.dependencies] [package.dependencies]
@ -17,7 +17,7 @@ SQLAlchemy = ">=1.3.0"
typing-extensions = ">=4" typing-extensions = ">=4"
[package.extras] [package.extras]
tz = ["backports.zoneinfo"] tz = ["backports.zoneinfo", "tzdata"]
[[package]] [[package]]
name = "annotated-types" name = "annotated-types"
@ -450,51 +450,53 @@ toml = ["tomli"]
[[package]] [[package]]
name = "cryptography" name = "cryptography"
version = "43.0.3" version = "44.0.0"
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
optional = false optional = false
python-versions = ">=3.7" python-versions = "!=3.9.0,!=3.9.1,>=3.7"
files = [ files = [
{file = "cryptography-43.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf7a1932ac4176486eab36a19ed4c0492da5d97123f1406cf15e41b05e787d2e"}, {file = "cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63efa177ff54aec6e1c0aefaa1a241232dcd37413835a9b674b6e3f0ae2bfd3e"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e1ce50266f4f70bf41a2c6dc4358afadae90e2a1e5342d3c08883df1675374f"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:443c4a81bb10daed9a8f334365fe52542771f25aedaf889fd323a853ce7377d6"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
{file = "cryptography-43.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:74f57f24754fe349223792466a709f8e0c093205ff0dca557af51072ff47ab18"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
{file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9762ea51a8fc2a88b70cf2995e5675b38d93bf36bd67d91721c309df184f49bd"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
{file = "cryptography-43.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:81ef806b1fef6b06dcebad789f988d3b37ccaee225695cf3e07648eee0fc6b73"}, {file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
{file = "cryptography-43.0.3-cp37-abi3-win32.whl", hash = "sha256:cbeb489927bd7af4aa98d4b261af9a5bc025bd87f0e3547e11584be9e9427be2"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
{file = "cryptography-43.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:f46304d6f0c6ab8e52770addfa2fc41e6629495548862279641972b6215451cd"}, {file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
{file = "cryptography-43.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8ac43ae87929a5982f5948ceda07001ee5e83227fd69cf55b109144938d96984"}, {file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
{file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:846da004a5804145a5f441b8530b4bf35afbf7da70f82409f151695b127213d5"}, {file = "cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd"},
{file = "cryptography-43.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f996e7268af62598f2fc1204afa98a3b5712313a55c4c9d434aef49cadc91d4"}, {file = "cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591"},
{file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:f7b178f11ed3664fd0e995a47ed2b5ff0a12d893e41dd0494f406d1cf555cab7"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7"},
{file = "cryptography-43.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:c2e6fc39c4ab499049df3bdf567f768a723a5e8464816e8f009f121a5a9f4405"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc"},
{file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:e1be4655c7ef6e1bbe6b5d0403526601323420bcf414598955968c9ef3eb7d16"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
{file = "cryptography-43.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:df6b6c6d742395dd77a23ea3728ab62f98379eff8fb61be2744d4679ab678f73"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
{file = "cryptography-43.0.3-cp39-abi3-win32.whl", hash = "sha256:d56e96520b1020449bbace2b78b603442e7e378a9b3bd68de65c782db1507995"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
{file = "cryptography-43.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:0c580952eef9bf68c4747774cde7ec1d85a6e61de97281f2dba83c7d2c806362"}, {file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
{file = "cryptography-43.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d03b5621a135bffecad2c73e9f4deb1a0f977b9a8ffe6f8e002bf6c9d07b918c"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
{file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a2a431ee15799d6db9fe80c82b055bae5a752bef645bba795e8e52687c69efe3"}, {file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
{file = "cryptography-43.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:281c945d0e28c92ca5e5930664c1cefd85efe80e5c0d2bc58dd63383fda29f83"}, {file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
{file = "cryptography-43.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f18c716be16bc1fea8e95def49edf46b82fccaa88587a45f8dc0ff6ab5d8e0a7"}, {file = "cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede"},
{file = "cryptography-43.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:4a02ded6cd4f0a5562a8887df8b3bd14e822a90f97ac5e544c162899bc467664"}, {file = "cryptography-44.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37d76e6863da3774cd9db5b409a9ecfd2c71c981c38788d3fcfaf177f447b731"},
{file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:53a583b6637ab4c4e3591a15bc9db855b8d9dee9a669b550f311480acab6eb08"}, {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:f677e1268c4e23420c3acade68fac427fffcb8d19d7df95ed7ad17cdef8404f4"},
{file = "cryptography-43.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1ec0bcf7e17c0c5669d881b1cd38c4972fade441b27bda1051665faaa89bdcaa"}, {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:f5e7cb1e5e56ca0933b4873c0220a78b773b24d40d186b6738080b73d3d0a756"},
{file = "cryptography-43.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ce6fae5bdad59577b44e4dfed356944fbf1d925269114c28be377692643b4ff"}, {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:8b3e6eae66cf54701ee7d9c83c30ac0a1e3fa17be486033000f2a73a12ab507c"},
{file = "cryptography-43.0.3.tar.gz", hash = "sha256:315b9001266a492a6ff443b61238f956b214dbec9910a081ba5b6646a055a805"}, {file = "cryptography-44.0.0-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:be4ce505894d15d5c5037167ffb7f0ae90b7be6f2a98f9a5c3442395501c32fa"},
{file = "cryptography-44.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:62901fb618f74d7d81bf408c8719e9ec14d863086efe4185afd07c352aee1d2c"},
{file = "cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02"},
] ]
[package.dependencies] [package.dependencies]
cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""} cffi = {version = ">=1.12", markers = "platform_python_implementation != \"PyPy\""}
[package.extras] [package.extras]
docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=3.0.0)"]
docstest = ["pyenchant (>=1.6.11)", "readme-renderer", "sphinxcontrib-spelling (>=4.0.1)"] docstest = ["pyenchant (>=3)", "readme-renderer (>=30.0)", "sphinxcontrib-spelling (>=7.3.1)"]
nox = ["nox"] nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2)"]
pep8test = ["check-sdist", "click", "mypy", "ruff"] pep8test = ["check-sdist", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"]
sdist = ["build"] sdist = ["build (>=1.0.0)"]
ssh = ["bcrypt (>=3.1.5)"] ssh = ["bcrypt (>=3.1.5)"]
test = ["certifi", "cryptography-vectors (==43.0.3)", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test = ["certifi (>=2024)", "cryptography-vectors (==44.0.0)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"]
test-randomorder = ["pytest-randomly"] test-randomorder = ["pytest-randomly"]
[[package]] [[package]]
@ -592,18 +594,18 @@ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt
[[package]] [[package]]
name = "filelock" name = "filelock"
version = "3.16.1" version = "3.17.0"
description = "A platform independent file lock." description = "A platform independent file lock."
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"}, {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"},
{file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"}, {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"},
] ]
[package.extras] [package.extras]
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"] docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
typing = ["typing-extensions (>=4.12.2)"] typing = ["typing-extensions (>=4.12.2)"]
[[package]] [[package]]
@ -769,15 +771,34 @@ http2 = ["h2 (>=3,<5)"]
socks = ["socksio (==1.*)"] socks = ["socksio (==1.*)"]
zstd = ["zstandard (>=0.18.0)"] zstd = ["zstandard (>=0.18.0)"]
[[package]]
name = "id"
version = "1.5.0"
description = "A tool for generating OIDC identities"
optional = false
python-versions = ">=3.8"
files = [
{file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"},
{file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"},
]
[package.dependencies]
requests = "*"
[package.extras]
dev = ["build", "bump (>=1.3.2)", "id[lint,test]"]
lint = ["bandit", "interrogate", "mypy", "ruff (<0.8.2)", "types-requests"]
test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"]
[[package]] [[package]]
name = "identify" name = "identify"
version = "2.6.5" version = "2.6.6"
description = "File identification library for Python" description = "File identification library for Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"}, {file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"},
{file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"}, {file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"},
] ]
[package.extras] [package.extras]
@ -799,13 +820,13 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
[[package]] [[package]]
name = "importlib-metadata" name = "importlib-metadata"
version = "8.5.0" version = "8.6.1"
description = "Read metadata from Python packages" description = "Read metadata from Python packages"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, {file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
{file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, {file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
] ]
[package.dependencies] [package.dependencies]
@ -817,7 +838,7 @@ cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
enabler = ["pytest-enabler (>=2.2)"] enabler = ["pytest-enabler (>=2.2)"]
perf = ["ipython"] perf = ["ipython"]
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
type = ["pytest-mypy"] type = ["pytest-mypy"]
[[package]] [[package]]
@ -1150,120 +1171,66 @@ files = [
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "2.0.2" version = "2.2.2"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece"},
{file = "numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04"},
{file = "numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66"},
{file = "numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b"},
{file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd"},
{file = "numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318"},
{file = "numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8"},
{file = "numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326"},
{file = "numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97"},
{file = "numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131"},
{file = "numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448"},
{file = "numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195"},
{file = "numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57"},
{file = "numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a"},
{file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669"},
{file = "numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951"},
{file = "numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9"},
{file = "numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15"},
{file = "numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4"},
{file = "numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc"},
{file = "numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b"},
{file = "numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e"},
{file = "numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c"},
{file = "numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c"},
{file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692"},
{file = "numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a"},
{file = "numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c"},
{file = "numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded"},
{file = "numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5"},
{file = "numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a"},
{file = "numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c"},
{file = "numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd"},
{file = "numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b"},
{file = "numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729"},
{file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1"},
{file = "numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd"},
{file = "numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d"},
{file = "numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d"},
{file = "numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa"},
{file = "numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73"},
{file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8"},
{file = "numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4"},
{file = "numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c"},
{file = "numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385"},
{file = "numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78"},
]
[[package]]
name = "numpy"
version = "2.2.1"
description = "Fundamental package for array computing in Python" description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.10" python-versions = ">=3.10"
files = [ files = [
{file = "numpy-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440"}, {file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"},
{file = "numpy-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab"}, {file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"},
{file = "numpy-2.2.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675"}, {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715"},
{file = "numpy-2.2.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308"}, {file = "numpy-2.2.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a"},
{file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957"}, {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97"},
{file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf"}, {file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957"},
{file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2"}, {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d"},
{file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528"}, {file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd"},
{file = "numpy-2.2.1-cp310-cp310-win32.whl", hash = "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95"}, {file = "numpy-2.2.2-cp310-cp310-win32.whl", hash = "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160"},
{file = "numpy-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf"}, {file = "numpy-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014"},
{file = "numpy-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484"}, {file = "numpy-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189"},
{file = "numpy-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7"}, {file = "numpy-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323"},
{file = "numpy-2.2.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb"}, {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac"},
{file = "numpy-2.2.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5"}, {file = "numpy-2.2.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e"},
{file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73"}, {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c"},
{file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591"}, {file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f"},
{file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8"}, {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826"},
{file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0"}, {file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8"},
{file = "numpy-2.2.1-cp311-cp311-win32.whl", hash = "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd"}, {file = "numpy-2.2.2-cp311-cp311-win32.whl", hash = "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50"},
{file = "numpy-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16"}, {file = "numpy-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2"},
{file = "numpy-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab"}, {file = "numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467"},
{file = "numpy-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa"}, {file = "numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a"},
{file = "numpy-2.2.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315"}, {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825"},
{file = "numpy-2.2.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355"}, {file = "numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37"},
{file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7"}, {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748"},
{file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d"}, {file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0"},
{file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51"}, {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278"},
{file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046"}, {file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba"},
{file = "numpy-2.2.1-cp312-cp312-win32.whl", hash = "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2"}, {file = "numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283"},
{file = "numpy-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8"}, {file = "numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb"},
{file = "numpy-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780"}, {file = "numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc"},
{file = "numpy-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821"}, {file = "numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369"},
{file = "numpy-2.2.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e"}, {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd"},
{file = "numpy-2.2.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348"}, {file = "numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be"},
{file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59"}, {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84"},
{file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af"}, {file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff"},
{file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51"}, {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0"},
{file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716"}, {file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de"},
{file = "numpy-2.2.1-cp313-cp313-win32.whl", hash = "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e"}, {file = "numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9"},
{file = "numpy-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60"}, {file = "numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369"},
{file = "numpy-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e"}, {file = "numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391"},
{file = "numpy-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712"}, {file = "numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39"},
{file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008"}, {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317"},
{file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84"}, {file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49"},
{file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631"}, {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2"},
{file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d"}, {file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7"},
{file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5"}, {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb"},
{file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71"}, {file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648"},
{file = "numpy-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2"}, {file = "numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4"},
{file = "numpy-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268"}, {file = "numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576"},
{file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3"}, {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495"},
{file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964"}, {file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df"},
{file = "numpy-2.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800"}, {file = "numpy-2.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a"},
{file = "numpy-2.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e"}, {file = "numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60"},
{file = "numpy-2.2.1.tar.gz", hash = "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918"}, {file = "numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"},
] ]
[[package]] [[package]]
@ -1494,20 +1461,6 @@ pip = ">=23.1.2"
graphviz = ["graphviz (>=0.20.1)"] graphviz = ["graphviz (>=0.20.1)"]
test = ["covdefaults (>=2.3)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "virtualenv (>=20.25,<21)"] test = ["covdefaults (>=2.3)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "virtualenv (>=20.25,<21)"]
[[package]]
name = "pkginfo"
version = "1.12.0"
description = "Query metadata from sdists / bdists / installed packages."
optional = false
python-versions = ">=3.8"
files = [
{file = "pkginfo-1.12.0-py3-none-any.whl", hash = "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088"},
{file = "pkginfo-1.12.0.tar.gz", hash = "sha256:8ad91a0445a036782b9366ef8b8c2c50291f83a553478ba8580c73d3215700cf"},
]
[package.extras]
testing = ["pytest", "pytest-cov", "wheel"]
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "4.3.6" version = "4.3.6"
@ -1576,13 +1529,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]] [[package]]
name = "pre-commit" name = "pre-commit"
version = "4.0.1" version = "4.1.0"
description = "A framework for managing and maintaining multi-language pre-commit hooks." description = "A framework for managing and maintaining multi-language pre-commit hooks."
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"}, {file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"},
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"}, {file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"},
] ]
[package.dependencies] [package.dependencies]
@ -2182,20 +2135,6 @@ async-timeout = {version = ">=4.0.3", markers = "python_full_version < \"3.11.3\
hiredis = ["hiredis (>=3.0.0)"] hiredis = ["hiredis (>=3.0.0)"]
ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==23.2.1)", "requests (>=2.31.0)"]
[[package]]
name = "reflex-chakra"
version = "0.6.2"
description = "reflex using chakra components"
optional = false
python-versions = "<4.0,>=3.9"
files = [
{file = "reflex_chakra-0.6.2-py3-none-any.whl", hash = "sha256:b8aa19f39a02601c560b97f4b17f171c0b5980e13a58069e3a5dd0999e362e4f"},
{file = "reflex_chakra-0.6.2.tar.gz", hash = "sha256:81ddb7f182cc454922cc817312755b799d4e1a49a46ef2e81305052dc76ef86d"},
]
[package.dependencies]
reflex = ">=0.6.0a"
[[package]] [[package]]
name = "reflex-hosting-cli" name = "reflex-hosting-cli"
version = "0.1.32" version = "0.1.32"
@ -2332,13 +2271,13 @@ jeepney = ">=0.6"
[[package]] [[package]]
name = "selenium" name = "selenium"
version = "4.27.1" version = "4.28.0"
description = "Official Python bindings for Selenium WebDriver" description = "Official Python bindings for Selenium WebDriver"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.9"
files = [ files = [
{file = "selenium-4.27.1-py3-none-any.whl", hash = "sha256:b89b1f62b5cfe8025868556fe82360d6b649d464f75d2655cb966c8f8447ea18"}, {file = "selenium-4.28.0-py3-none-any.whl", hash = "sha256:3d6a2e8e1b850a1078884ea19f4e011ecdc12263434d87a0b78769836fb82dd8"},
{file = "selenium-4.27.1.tar.gz", hash = "sha256:5296c425a75ff1b44d0d5199042b36a6d1ef76c04fb775b97b40be739a9caae2"}, {file = "selenium-4.28.0.tar.gz", hash = "sha256:a9fae6eef48d470a1b0c6e45185d96f0dafb025e8da4b346cc41e4da3ac54fa0"},
] ]
[package.dependencies] [package.dependencies]
@ -2540,7 +2479,6 @@ files = [
[package.dependencies] [package.dependencies]
anyio = ">=3.4.0,<5" anyio = ">=3.4.0,<5"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras] [package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
@ -2709,20 +2647,19 @@ wsproto = ">=0.14"
[[package]] [[package]]
name = "twine" name = "twine"
version = "6.0.1" version = "6.1.0"
description = "Collection of utilities for publishing packages on PyPI" description = "Collection of utilities for publishing packages on PyPI"
optional = false optional = false
python-versions = ">=3.8" python-versions = ">=3.8"
files = [ files = [
{file = "twine-6.0.1-py3-none-any.whl", hash = "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218"}, {file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"},
{file = "twine-6.0.1.tar.gz", hash = "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27"}, {file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"},
] ]
[package.dependencies] [package.dependencies]
importlib-metadata = {version = ">=3.6", markers = "python_version < \"3.10\""} id = "*"
keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""} keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""}
packaging = "*" packaging = ">=24.0"
pkginfo = ">=1.8.1"
readme-renderer = ">=35.0" readme-renderer = ">=35.0"
requests = ">=2.20" requests = ">=2.20"
requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0"
@ -2763,13 +2700,13 @@ files = [
[[package]] [[package]]
name = "tzdata" name = "tzdata"
version = "2024.2" version = "2025.1"
description = "Provider of IANA time zone data" description = "Provider of IANA time zone data"
optional = false optional = false
python-versions = ">=2" python-versions = ">=2"
files = [ files = [
{file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"}, {file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"},
{file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"}, {file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"},
] ]
[[package]] [[package]]
@ -2849,80 +2786,80 @@ test = ["websockets"]
[[package]] [[package]]
name = "websockets" name = "websockets"
version = "14.1" version = "14.2"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"}, {file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"},
{file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"}, {file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"},
{file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"}, {file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"},
{file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"}, {file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"},
{file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"}, {file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"},
{file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"}, {file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"},
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"}, {file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"},
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"}, {file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"},
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"}, {file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"},
{file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"}, {file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"},
{file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"}, {file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"},
{file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"}, {file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"},
{file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"}, {file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"},
{file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"}, {file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"},
{file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"}, {file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"},
{file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"}, {file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"},
{file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"}, {file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"},
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"}, {file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"},
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"}, {file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"},
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"}, {file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"},
{file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"}, {file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"},
{file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"}, {file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"},
{file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"}, {file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"},
{file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"}, {file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"},
{file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"}, {file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"},
{file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"}, {file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"},
{file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"}, {file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"},
{file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"}, {file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"},
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"}, {file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"},
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"}, {file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"},
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"}, {file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"},
{file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"}, {file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"},
{file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"}, {file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"},
{file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"}, {file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"},
{file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"}, {file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"},
{file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"}, {file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"},
{file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"}, {file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"},
{file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"}, {file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"},
{file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"}, {file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"},
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"}, {file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"},
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"}, {file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"},
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"}, {file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"},
{file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"}, {file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"},
{file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"}, {file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"},
{file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"}, {file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"},
{file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"}, {file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"},
{file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"}, {file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"},
{file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"}, {file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"},
{file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"}, {file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"},
{file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"}, {file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"},
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"}, {file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"},
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"}, {file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"},
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"}, {file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"},
{file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"}, {file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"},
{file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"}, {file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"},
{file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"}, {file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"},
{file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"}, {file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"},
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"}, {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"},
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"}, {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"},
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"}, {file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"},
{file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"}, {file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"},
{file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"}, {file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"},
{file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"}, {file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"},
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"}, {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"},
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"}, {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"},
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"}, {file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"},
{file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"}, {file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"},
{file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"}, {file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"},
{file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"}, {file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"},
] ]
[[package]] [[package]]
@ -3062,5 +2999,5 @@ type = ["pytest-mypy"]
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.9" python-versions = "^3.10"
content-hash = "ccd6d6b00fdcf40562854380fafdb18c990b7f6a4f2883b33aaeb0351fcdbc06" content-hash = "353bcb291d6d423963b380f3ee33769106487cacd7f8e34d6c420f2a87a66508"

View File

@ -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]
@ -88,8 +87,8 @@ reportIncompatibleVariableOverride = false
target-version = "py39" target-version = "py39"
output-format = "concise" output-format = "concise"
lint.isort.split-on-trailing-comma = false lint.isort.split-on-trailing-comma = false
lint.select = ["B", "C4", "D", "E", "ERA", "F", "FURB", "I", "PERF", "PTH", "RUF", "SIM", "T", "W"] lint.select = ["B", "C4", "D", "E", "ERA", "F", "FURB", "I", "PERF", "PTH", "RUF", "SIM", "T", "TRY", "W"]
lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF012"] lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF012", "TRY0"]
lint.pydocstyle.convention = "google" lint.pydocstyle.convention = "google"
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
@ -104,5 +103,5 @@ asyncio_default_fixture_loop_scope = "function"
asyncio_mode = "auto" asyncio_mode = "auto"
[tool.codespell] [tool.codespell]
skip = "docs/*,*.html,examples/*, *.pyi, *.lock" skip = "docs/*,*.html,examples/*, *.pyi, poetry.lock"
ignore-words-list = "te, TreeE" ignore-words-list = "te, TreeE"

View File

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

View File

@ -3,6 +3,7 @@ import axios from "axios";
import io from "socket.io-client"; import io from "socket.io-client";
import JSON5 from "json5"; import JSON5 from "json5";
import env from "$/env.json"; import env from "$/env.json";
import reflexEnvironment from "$/reflex.json";
import Cookies from "universal-cookie"; import Cookies from "universal-cookie";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import Router, { useRouter } from "next/router"; import Router, { useRouter } from "next/router";
@ -404,6 +405,7 @@ export const connect = async (
socket.current = io(endpoint.href, { socket.current = io(endpoint.href, {
path: endpoint["pathname"], path: endpoint["pathname"],
transports: transports, transports: transports,
protocols: env.TEST_MODE ? undefined : [reflexEnvironment.version],
autoUnref: false, autoUnref: false,
}); });
// Ensure undefined fields in events are sent as null instead of removed // Ensure undefined fields in events are sent as null instead of removed

View File

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

View File

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

View File

@ -463,14 +463,8 @@ class App(MiddlewareMixin, LifespanMixin):
Returns: Returns:
The generated component. The generated component.
Raises:
exceptions.MatchTypeError: If the return types of match cases in rx.match are different.
""" """
try: return component if isinstance(component, Component) else component()
return component if isinstance(component, Component) else component()
except exceptions.MatchTypeError:
raise
def add_page( def add_page(
self, self,
@ -564,11 +558,12 @@ class App(MiddlewareMixin, LifespanMixin):
meta=meta, meta=meta,
) )
def _compile_page(self, route: str): def _compile_page(self, route: str, save_page: bool = True):
"""Compile a page. """Compile a page.
Args: Args:
route: The route of the page to compile. route: The route of the page to compile.
save_page: If True, the compiled page is saved to self.pages.
""" """
component, enable_state = compiler.compile_unevaluated_page( component, enable_state = compiler.compile_unevaluated_page(
route, self.unevaluated_pages[route], self.state, self.style, self.theme route, self.unevaluated_pages[route], self.state, self.style, self.theme
@ -579,7 +574,8 @@ class App(MiddlewareMixin, LifespanMixin):
# Add the page. # Add the page.
self._check_routes_conflict(route) self._check_routes_conflict(route)
self.pages[route] = component if save_page:
self.pages[route] = component
def get_load_events(self, route: str) -> list[IndividualEventType[[], Any]]: def get_load_events(self, route: str) -> list[IndividualEventType[[], Any]]:
"""Get the load events for a route. """Get the load events for a route.
@ -879,14 +875,16 @@ class App(MiddlewareMixin, LifespanMixin):
# If a theme component was provided, wrap the app with it # If a theme component was provided, wrap the app with it
app_wrappers[(20, "Theme")] = self.theme app_wrappers[(20, "Theme")] = self.theme
should_compile = self._should_compile()
for route in self.unevaluated_pages: for route in self.unevaluated_pages:
console.debug(f"Evaluating page: {route}") console.debug(f"Evaluating page: {route}")
self._compile_page(route) self._compile_page(route, save_page=should_compile)
# Add the optional endpoints (_upload) # Add the optional endpoints (_upload)
self._add_optional_endpoints() self._add_optional_endpoints()
if not self._should_compile(): if not should_compile:
return return
self._validate_var_dependencies() self._validate_var_dependencies()
@ -1530,7 +1528,11 @@ class EventNamespace(AsyncNamespace):
sid: The Socket.IO session id. sid: The Socket.IO session id.
environ: The request information, including HTTP headers. environ: The request information, including HTTP headers.
""" """
pass subprotocol = environ.get("HTTP_SEC_WEBSOCKET_PROTOCOL", None)
if subprotocol and subprotocol != constants.Reflex.VERSION:
console.warn(
f"Frontend version {subprotocol} for session {sid} does not match the backend version {constants.Reflex.VERSION}."
)
def on_disconnect(self, sid): def on_disconnect(self, sid):
"""Event for when the websocket disconnects. """Event for when the websocket disconnects.
@ -1563,10 +1565,36 @@ class EventNamespace(AsyncNamespace):
Args: Args:
sid: The Socket.IO session id. sid: The Socket.IO session id.
data: The event data. data: The event data.
Raises:
EventDeserializationError: If the event data is not a dictionary.
""" """
fields = data fields = data
# Get the event.
event = Event(**{k: v for k, v in fields.items() if k in _EVENT_FIELDS}) if isinstance(fields, str):
console.warn(
"Received event data as a string. This generally should not happen and may indicate a bug."
f" Event data: {fields}"
)
try:
fields = json.loads(fields)
except json.JSONDecodeError as ex:
raise exceptions.EventDeserializationError(
f"Failed to deserialize event data: {fields}."
) from ex
if not isinstance(fields, dict):
raise exceptions.EventDeserializationError(
f"Event data must be a dictionary, but received {fields} of type {type(fields)}."
)
try:
# Get the event.
event = Event(**{k: v for k, v in fields.items() if k in _EVENT_FIELDS})
except (TypeError, ValueError) as ex:
raise exceptions.EventDeserializationError(
f"Failed to deserialize event data: {fields}."
) from ex
self.token_to_sid[event.token] = sid self.token_to_sid[event.token] = sid
self.sid_to_token[sid] = event.token self.sid_to_token[sid] = event.token

View File

@ -7,14 +7,13 @@ from concurrent.futures import ThreadPoolExecutor
from reflex import constants from reflex import constants
from reflex.utils import telemetry from reflex.utils import telemetry
from reflex.utils.exec import is_prod_mode from reflex.utils.exec import is_prod_mode
from reflex.utils.prerequisites import get_app from reflex.utils.prerequisites import get_and_validate_app
if constants.CompileVars.APP != "app": if constants.CompileVars.APP != "app":
raise AssertionError("unexpected variable name for 'app'") raise AssertionError("unexpected variable name for 'app'")
telemetry.send("compile") telemetry.send("compile")
app_module = get_app(reload=False) app, app_module = get_and_validate_app(reload=False)
app = getattr(app_module, constants.CompileVars.APP)
# For py3.9 compatibility when redis is used, we MUST add any decorator pages # For py3.9 compatibility when redis is used, we MUST add any decorator pages
# before compiling the app in a thread to avoid event loop error (REF-2172). # before compiling the app in a thread to avoid event loop error (REF-2172).
app._apply_decorated_pages() app._apply_decorated_pages()
@ -30,7 +29,7 @@ if is_prod_mode():
# ensure only "app" is exposed. # ensure only "app" is exposed.
del app_module del app_module
del compile_future del compile_future
del get_app del get_and_validate_app
del is_prod_mode del is_prod_mode
del telemetry del telemetry
del constants del constants

View File

@ -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,
@ -428,20 +425,22 @@ class Component(BaseComponent, ABC):
else: else:
continue continue
def determine_key(value):
# Try to create a var from the value
key = value if isinstance(value, Var) else LiteralVar.create(value)
# Check that the var type is not None.
if key is None:
raise TypeError
return key
# Check whether the key is a component prop. # Check whether the key is a component prop.
if types._issubclass(field_type, Var): if types._issubclass(field_type, Var):
# Used to store the passed types if var type is a union. # Used to store the passed types if var type is a union.
passed_types = None passed_types = None
try: try:
# Try to create a var from the value. kwargs[key] = determine_key(value)
if isinstance(value, Var):
kwargs[key] = value
else:
kwargs[key] = LiteralVar.create(value)
# Check that the var type is not None.
if kwargs[key] is None:
raise TypeError
expected_type = fields[key].outer_type_.__args__[0] expected_type = fields[key].outer_type_.__args__[0]
# validate literal fields. # validate literal fields.
@ -544,41 +543,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]]:
@ -739,22 +703,21 @@ class Component(BaseComponent, ABC):
# Import here to avoid circular imports. # Import here to avoid circular imports.
from reflex.components.base.bare import Bare from reflex.components.base.bare import Bare
from reflex.components.base.fragment import Fragment from reflex.components.base.fragment import Fragment
from reflex.utils.exceptions import ComponentTypeError from reflex.utils.exceptions import ChildrenTypeError
# Filter out None props # Filter out None props
props = {key: value for key, value in props.items() if value is not None} props = {key: value for key, value in props.items() if value is not None}
def validate_children(children): def validate_children(children):
for child in children: for child in children:
if isinstance(child, tuple): if isinstance(child, (tuple, list)):
validate_children(child) validate_children(child)
# Make sure the child is a valid type. # Make sure the child is a valid type.
if not types._isinstance(child, ComponentChild): if isinstance(child, dict) or not types._isinstance(
raise ComponentTypeError( child, ComponentChild
"Children of Reflex components must be other components, " ):
"state vars, or primitive Python types. " raise ChildrenTypeError(component=cls.__name__, child=child)
f"Got child {child} of type {type(child)}.",
)
# Validate all the children. # Validate all the children.
validate_children(children) validate_children(children)

View File

@ -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 props.get("theme") is not None 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

View File

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

View File

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

View File

@ -2,13 +2,15 @@
from reflex.components.component import Component from reflex.components.component import Component
from reflex.utils import format from reflex.utils import format
from reflex.vars.base import Var from reflex.utils.imports import ImportVar
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralStringVar
class LucideIconComponent(Component): class LucideIconComponent(Component):
"""Lucide Icon Component.""" """Lucide Icon Component."""
library = "lucide-react@0.469.0" library = "lucide-react@0.471.1"
class Icon(LucideIconComponent): class Icon(LucideIconComponent):
@ -32,6 +34,7 @@ class Icon(LucideIconComponent):
Raises: Raises:
AttributeError: The errors tied to bad usage of the Icon component. AttributeError: The errors tied to bad usage of the Icon component.
ValueError: If the icon tag is invalid. ValueError: If the icon tag is invalid.
TypeError: If the icon name is not a string.
Returns: Returns:
The created component. The created component.
@ -39,7 +42,6 @@ class Icon(LucideIconComponent):
if children: if children:
if len(children) == 1 and isinstance(children[0], str): if len(children) == 1 and isinstance(children[0], str):
props["tag"] = children[0] props["tag"] = children[0]
children = []
else: else:
raise AttributeError( raise AttributeError(
f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix" f"Passing multiple children to Icon component is not allowed: remove positional arguments {children[1:]} to fix"
@ -47,24 +49,46 @@ class Icon(LucideIconComponent):
if "tag" not in props: if "tag" not in props:
raise AttributeError("Missing 'tag' keyword-argument for Icon") raise AttributeError("Missing 'tag' keyword-argument for Icon")
tag: str | Var | LiteralVar = props.pop("tag")
if isinstance(tag, LiteralVar):
if isinstance(tag, LiteralStringVar):
tag = tag._var_value
else:
raise TypeError(f"Icon name must be a string, got {type(tag)}")
elif isinstance(tag, Var):
return DynamicIcon.create(name=tag, **props)
if ( if (
not isinstance(props["tag"], str) not isinstance(tag, str)
or format.to_snake_case(props["tag"]) not in LUCIDE_ICON_LIST or format.to_snake_case(tag) not in LUCIDE_ICON_LIST
): ):
raise ValueError( raise ValueError(
f"Invalid icon tag: {props['tag']}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..." f"Invalid icon tag: {tag}. Please use one of the following: {', '.join(LUCIDE_ICON_LIST[0:25])}, ..."
"\nSee full list at https://lucide.dev/icons." "\nSee full list at https://lucide.dev/icons."
) )
if props["tag"] in LUCIDE_ICON_MAPPING_OVERRIDE: if tag in LUCIDE_ICON_MAPPING_OVERRIDE:
props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[props["tag"]] props["tag"] = LUCIDE_ICON_MAPPING_OVERRIDE[tag]
else: else:
props["tag"] = ( props["tag"] = format.to_title_case(format.to_snake_case(tag)) + "Icon"
format.to_title_case(format.to_snake_case(props["tag"])) + "Icon"
)
props["alias"] = f"Lucide{props['tag']}" props["alias"] = f"Lucide{props['tag']}"
props.setdefault("color", "var(--current-color)") props.setdefault("color", "var(--current-color)")
return super().create(*children, **props) return super().create(**props)
class DynamicIcon(LucideIconComponent):
"""A DynamicIcon component."""
tag = "DynamicIcon"
name: Var[str]
def _get_imports(self):
_imports = super()._get_imports()
if self.library:
_imports.pop(self.library)
_imports["lucide-react/dynamic"] = [ImportVar("DynamicIcon", install=False)]
return _imports
LUCIDE_ICON_LIST = [ LUCIDE_ICON_LIST = [
@ -846,6 +870,7 @@ LUCIDE_ICON_LIST = [
"house", "house",
"house_plug", "house_plug",
"house_plus", "house_plus",
"house_wifi",
"ice_cream_bowl", "ice_cream_bowl",
"ice_cream_cone", "ice_cream_cone",
"id_card", "id_card",
@ -1534,6 +1559,7 @@ LUCIDE_ICON_LIST = [
"trending_up_down", "trending_up_down",
"triangle", "triangle",
"triangle_alert", "triangle_alert",
"triangle_dashed",
"triangle_right", "triangle_right",
"trophy", "trophy",
"truck", "truck",

View File

@ -104,12 +104,60 @@ class Icon(LucideIconComponent):
Raises: Raises:
AttributeError: The errors tied to bad usage of the Icon component. AttributeError: The errors tied to bad usage of the Icon component.
ValueError: If the icon tag is invalid. ValueError: If the icon tag is invalid.
TypeError: If the icon name is not a string.
Returns: Returns:
The created component. The created component.
""" """
... ...
class DynamicIcon(LucideIconComponent):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
name: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[[], BASE_STATE]] = None,
on_click: Optional[EventType[[], BASE_STATE]] = None,
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
on_focus: Optional[EventType[[], BASE_STATE]] = None,
on_mount: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props,
) -> "DynamicIcon":
"""Create the component.
Args:
*children: The children of the component.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The props of the component.
Returns:
The component.
"""
...
LUCIDE_ICON_LIST = [ LUCIDE_ICON_LIST = [
"a_arrow_down", "a_arrow_down",
"a_arrow_up", "a_arrow_up",
@ -889,6 +937,7 @@ LUCIDE_ICON_LIST = [
"house", "house",
"house_plug", "house_plug",
"house_plus", "house_plus",
"house_wifi",
"ice_cream_bowl", "ice_cream_bowl",
"ice_cream_cone", "ice_cream_cone",
"id_card", "id_card",
@ -1577,6 +1626,7 @@ LUCIDE_ICON_LIST = [
"trending_up_down", "trending_up_down",
"triangle", "triangle",
"triangle_alert", "triangle_alert",
"triangle_dashed",
"triangle_right", "triangle_right",
"trophy", "trophy",
"truck", "truck",

View File

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

View File

@ -70,6 +70,8 @@ _SUBMOD_ATTRS: dict = {
"Label", "Label",
"label_list", "label_list",
"LabelList", "LabelList",
"cell",
"Cell",
], ],
"polar": [ "polar": [
"pie", "pie",

View File

@ -53,11 +53,13 @@ from .charts import radar_chart as radar_chart
from .charts import radial_bar_chart as radial_bar_chart from .charts import radial_bar_chart as radial_bar_chart
from .charts import scatter_chart as scatter_chart from .charts import scatter_chart as scatter_chart
from .charts import treemap as treemap from .charts import treemap as treemap
from .general import Cell as Cell
from .general import GraphingTooltip as GraphingTooltip from .general import GraphingTooltip as GraphingTooltip
from .general import Label as Label from .general import Label as Label
from .general import LabelList as LabelList from .general import LabelList as LabelList
from .general import Legend as Legend from .general import Legend as Legend
from .general import ResponsiveContainer as ResponsiveContainer from .general import ResponsiveContainer as ResponsiveContainer
from .general import cell as cell
from .general import graphing_tooltip as graphing_tooltip from .general import graphing_tooltip as graphing_tooltip
from .general import label as label from .general import label as label
from .general import label_list as label_list from .general import label_list as label_list

View File

@ -88,11 +88,13 @@ class ChartBase(RechartsCharts):
"width": width if width is not None else "100%", "width": width if width is not None else "100%",
"height": height if height is not None else "100%", "height": height if height is not None else "100%",
} }
# Provide min dimensions so the graph always appears, even if the outer container is zero-size.
if width is None: # Ensure that the min_height and min_width are set to prevent the chart from collapsing.
dim_props["min_width"] = 200 # We are using small values so that height and width can still be used over min_height and min_width.
if height is None: # Without this, sometimes the chart will not be visible. Causing confusion to the user.
dim_props["min_height"] = 100 # With this, the user will see a small chart and can adjust the height and width and can figure out that the issue is with the size.
dim_props["min_height"] = props.pop("min_height", 10)
dim_props["min_width"] = props.pop("min_width", 10)
return ResponsiveContainer.create( return ResponsiveContainer.create(
super().create(*children, **props), super().create(*children, **props),

View File

@ -36,11 +36,11 @@ class ResponsiveContainer(Recharts, MemoizationLeaf):
# The height of chart container. Can be a number or string. Default: "100%" # The height of chart container. Can be a number or string. Default: "100%"
height: Var[Union[int, str]] height: Var[Union[int, str]]
# The minimum width of chart container. Number # The minimum width of chart container. Number or string.
min_width: Var[int] min_width: Var[Union[int, str]]
# The minimum height of chart container. Number # The minimum height of chart container. Number or string.
min_height: Var[int] min_height: Var[Union[int, str]]
# If specified a positive number, debounced function will be used to handle the resize event. Default: 0 # If specified a positive number, debounced function will be used to handle the resize event. Default: 0
debounce: Var[int] debounce: Var[int]
@ -242,8 +242,23 @@ class LabelList(Recharts):
stroke: Var[Union[str, Color]] = LiteralVar.create("none") stroke: Var[Union[str, Color]] = LiteralVar.create("none")
class Cell(Recharts):
"""A Cell component in Recharts."""
tag = "Cell"
alias = "RechartsCell"
# The presentation attribute of a rectangle in bar or a sector in pie.
fill: Var[str]
# The presentation attribute of a rectangle in bar or a sector in pie.
stroke: Var[str]
responsive_container = ResponsiveContainer.create responsive_container = ResponsiveContainer.create
legend = Legend.create legend = Legend.create
graphing_tooltip = GraphingTooltip.create graphing_tooltip = GraphingTooltip.create
label = Label.create label = Label.create
label_list = LabelList.create label_list = LabelList.create
cell = Cell.create

View File

@ -22,8 +22,8 @@ class ResponsiveContainer(Recharts, MemoizationLeaf):
aspect: Optional[Union[Var[int], int]] = None, aspect: Optional[Union[Var[int], int]] = None,
width: Optional[Union[Var[Union[int, str]], int, str]] = None, width: Optional[Union[Var[Union[int, str]], int, str]] = None,
height: Optional[Union[Var[Union[int, str]], int, str]] = None, height: Optional[Union[Var[Union[int, str]], int, str]] = None,
min_width: Optional[Union[Var[int], int]] = None, min_width: Optional[Union[Var[Union[int, str]], int, str]] = None,
min_height: Optional[Union[Var[int], int]] = None, min_height: Optional[Union[Var[Union[int, str]], int, str]] = None,
debounce: Optional[Union[Var[int], int]] = None, debounce: Optional[Union[Var[int], int]] = None,
style: Optional[Style] = None, style: Optional[Style] = None,
key: Optional[Any] = None, key: Optional[Any] = None,
@ -56,8 +56,8 @@ class ResponsiveContainer(Recharts, MemoizationLeaf):
aspect: The aspect ratio of the container. The final aspect ratio of the SVG element will be (width / height) * aspect. Number aspect: The aspect ratio of the container. The final aspect ratio of the SVG element will be (width / height) * aspect. Number
width: The width of chart container. Can be a number or string. Default: "100%" width: The width of chart container. Can be a number or string. Default: "100%"
height: The height of chart container. Can be a number or string. Default: "100%" height: The height of chart container. Can be a number or string. Default: "100%"
min_width: The minimum width of chart container. Number min_width: The minimum width of chart container. Number or string.
min_height: The minimum height of chart container. Number min_height: The minimum height of chart container. Number or string.
debounce: If specified a positive number, debounced function will be used to handle the resize event. Default: 0 debounce: If specified a positive number, debounced function will be used to handle the resize event. Default: 0
on_resize: If specified provides a callback providing the updated chart width and height values. on_resize: If specified provides a callback providing the updated chart width and height values.
style: The style of the component. style: The style of the component.
@ -482,8 +482,59 @@ class LabelList(Recharts):
""" """
... ...
class Cell(Recharts):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
fill: Optional[Union[Var[str], str]] = None,
stroke: Optional[Union[Var[str], str]] = None,
style: Optional[Style] = None,
key: Optional[Any] = None,
id: Optional[Any] = None,
class_name: Optional[Any] = None,
autofocus: Optional[bool] = None,
custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
on_blur: Optional[EventType[[], BASE_STATE]] = None,
on_click: Optional[EventType[[], BASE_STATE]] = None,
on_context_menu: Optional[EventType[[], BASE_STATE]] = None,
on_double_click: Optional[EventType[[], BASE_STATE]] = None,
on_focus: Optional[EventType[[], BASE_STATE]] = None,
on_mount: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_down: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_enter: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_leave: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_move: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_out: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_over: Optional[EventType[[], BASE_STATE]] = None,
on_mouse_up: Optional[EventType[[], BASE_STATE]] = None,
on_scroll: Optional[EventType[[], BASE_STATE]] = None,
on_unmount: Optional[EventType[[], BASE_STATE]] = None,
**props,
) -> "Cell":
"""Create the component.
Args:
*children: The children of the component.
fill: The presentation attribute of a rectangle in bar or a sector in pie.
stroke: The presentation attribute of a rectangle in bar or a sector in pie.
style: The style of the component.
key: A unique key for the component.
id: The id for the component.
class_name: The class name for the component.
autofocus: Whether the component should take the focus once the page is loaded
custom_attrs: custom attribute
**props: The props of the component.
Returns:
The component.
"""
...
responsive_container = ResponsiveContainer.create responsive_container = ResponsiveContainer.create
legend = Legend.create legend = Legend.create
graphing_tooltip = GraphingTooltip.create graphing_tooltip = GraphingTooltip.create
label = Label.create label = Label.create
label_list = LabelList.create label_list = LabelList.create
cell = Cell.create

View File

@ -12,6 +12,7 @@ import threading
import urllib.parse import urllib.parse
from importlib.util import find_spec from importlib.util import find_spec
from pathlib import Path from pathlib import Path
from types import ModuleType
from typing import ( from typing import (
TYPE_CHECKING, TYPE_CHECKING,
Any, Any,
@ -607,6 +608,9 @@ class Config(Base):
# The name of the app (should match the name of the app directory). # The name of the app (should match the name of the app directory).
app_name: str app_name: str
# The path to the app module.
app_module_import: Optional[str] = None
# The log level to use. # The log level to use.
loglevel: constants.LogLevel = constants.LogLevel.DEFAULT loglevel: constants.LogLevel = constants.LogLevel.DEFAULT
@ -729,6 +733,19 @@ class Config(Base):
"REDIS_URL is required when using the redis state manager." "REDIS_URL is required when using the redis state manager."
) )
@property
def app_module(self) -> ModuleType | None:
"""Return the app module if `app_module_import` is set.
Returns:
The app module.
"""
return (
importlib.import_module(self.app_module_import)
if self.app_module_import
else None
)
@property @property
def module(self) -> str: def module(self) -> str:
"""Get the module name of the app. """Get the module name of the app.
@ -736,6 +753,8 @@ class Config(Base):
Returns: Returns:
The module name. The module name.
""" """
if self.app_module is not None:
return self.app_module.__name__
return ".".join([self.app_name, self.app_name]) return ".".join([self.app_name, self.app_name])
def update_from_env(self) -> dict[str, Any]: def update_from_env(self) -> dict[str, Any]:
@ -874,7 +893,7 @@ def get_config(reload: bool = False) -> Config:
return cached_rxconfig.config return cached_rxconfig.config
with _config_lock: with _config_lock:
sys_path = sys.path.copy() orig_sys_path = sys.path.copy()
sys.path.clear() sys.path.clear()
sys.path.append(str(Path.cwd())) sys.path.append(str(Path.cwd()))
try: try:
@ -882,9 +901,14 @@ def get_config(reload: bool = False) -> Config:
return _get_config() return _get_config()
except Exception: except Exception:
# If the module import fails, try to import with the original sys.path. # If the module import fails, try to import with the original sys.path.
sys.path.extend(sys_path) sys.path.extend(orig_sys_path)
return _get_config() return _get_config()
finally: finally:
# Find any entries added to sys.path by rxconfig.py itself.
extra_paths = [
p for p in sys.path if p not in orig_sys_path and p != str(Path.cwd())
]
# Restore the original sys.path. # Restore the original sys.path.
sys.path.clear() sys.path.clear()
sys.path.extend(sys_path) sys.path.extend(extra_paths)
sys.path.extend(orig_sys_path)

View File

@ -1,6 +1,7 @@
"""The constants package.""" """The constants package."""
from .base import ( from .base import (
APP_HARNESS_FLAG,
COOKIES, COOKIES,
IS_LINUX, IS_LINUX,
IS_MACOS, IS_MACOS,

View File

@ -257,6 +257,7 @@ SESSION_STORAGE = "session_storage"
# Testing variables. # Testing variables.
# Testing os env set by pytest when running a test case. # Testing os env set by pytest when running a test case.
PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST" PYTEST_CURRENT_TEST = "PYTEST_CURRENT_TEST"
APP_HARNESS_FLAG = "APP_HARNESS_FLAG"
REFLEX_VAR_OPENING_TAG = "<reflex.Var>" REFLEX_VAR_OPENING_TAG = "<reflex.Var>"
REFLEX_VAR_CLOSING_TAG = "</reflex.Var>" REFLEX_VAR_CLOSING_TAG = "</reflex.Var>"

View File

@ -421,12 +421,13 @@ def _run_commands_in_subprocess(cmds: list[str]) -> bool:
console.debug(f"Running command: {' '.join(cmds)}") console.debug(f"Running command: {' '.join(cmds)}")
try: try:
result = subprocess.run(cmds, capture_output=True, text=True, check=True) result = subprocess.run(cmds, capture_output=True, text=True, check=True)
console.debug(result.stdout)
return True
except subprocess.CalledProcessError as cpe: except subprocess.CalledProcessError as cpe:
console.error(cpe.stdout) console.error(cpe.stdout)
console.error(cpe.stderr) console.error(cpe.stderr)
return False return False
else:
console.debug(result.stdout)
return True
def _make_pyi_files(): def _make_pyi_files():
@ -931,10 +932,11 @@ def _get_file_from_prompt_in_loop() -> Tuple[bytes, str] | None:
file_extension = image_filepath.suffix file_extension = image_filepath.suffix
try: try:
image_file = image_filepath.read_bytes() image_file = image_filepath.read_bytes()
return image_file, file_extension
except OSError as ose: except OSError as ose:
console.error(f"Unable to read the {file_extension} file due to {ose}") console.error(f"Unable to read the {file_extension} file due to {ose}")
raise typer.Exit(code=1) from ose raise typer.Exit(code=1) from ose
else:
return image_file, file_extension
console.debug(f"File extension detected: {file_extension}") console.debug(f"File extension detected: {file_extension}")
return None return None

View File

@ -42,7 +42,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
@ -98,32 +102,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,
@ -821,10 +799,9 @@ 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: ... ) -> EventSpec: ...
@ -851,26 +828,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),
@ -1287,11 +1248,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
@ -1328,10 +1292,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(
@ -1343,43 +1312,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])
@ -1401,15 +1362,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
@ -1428,26 +1384,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
@ -1469,7 +1425,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
] ]
@ -1485,7 +1447,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.
@ -1495,7 +1457,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
@ -1511,7 +1473,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"
@ -1599,7 +1561,7 @@ def get_handler_args(
def fix_events( def fix_events(
events: list[EventHandler | EventSpec] | None, events: list[EventSpec | EventHandler] | None,
token: str, token: str,
router_data: dict[str, Any] | None = None, router_data: dict[str, Any] | None = None,
) -> list[Event]: ) -> list[Event]:
@ -1820,8 +1782,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."""
@ -2025,6 +1985,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.
""" """
@ -2033,7 +1996,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:

View File

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

View File

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

View File

@ -306,6 +306,9 @@ def export(
help="Whether to exclude sqlite db files when exporting backend.", help="Whether to exclude sqlite db files when exporting backend.",
hidden=True, hidden=True,
), ),
env: constants.Env = typer.Option(
constants.Env.PROD, help="The environment to export the app in."
),
loglevel: constants.LogLevel = typer.Option( loglevel: constants.LogLevel = typer.Option(
config.loglevel, help="The log level to use." config.loglevel, help="The log level to use."
), ),
@ -323,6 +326,7 @@ def export(
backend=backend, backend=backend,
zip_dest_dir=zip_dest_dir, zip_dest_dir=zip_dest_dir,
upload_db_file=upload_db_file, upload_db_file=upload_db_file,
env=env,
loglevel=loglevel.subprocess_level(), loglevel=loglevel.subprocess_level(),
) )
@ -440,7 +444,11 @@ def deploy(
config.app_name, config.app_name,
"--app-name", "--app-name",
help="The name of the App to deploy under.", help="The name of the App to deploy under.",
hidden=True, ),
app_id: str = typer.Option(
None,
"--app-id",
help="The ID of the App to deploy over.",
), ),
regions: List[str] = typer.Option( regions: List[str] = typer.Option(
[], [],
@ -480,6 +488,11 @@ def deploy(
"--project", "--project",
help="project id to deploy to", help="project id to deploy to",
), ),
project_name: Optional[str] = typer.Option(
None,
"--project-name",
help="The name of the project to deploy to.",
),
token: Optional[str] = typer.Option( token: Optional[str] = typer.Option(
None, None,
"--token", "--token",
@ -492,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
@ -503,12 +517,20 @@ def deploy(
# Set the log level. # Set the log level.
console.set_log_level(loglevel) console.set_log_level(loglevel)
if not token: def convert_reflex_loglevel_to_reflex_cli_loglevel(
# make sure user is logged in. loglevel: constants.LogLevel,
if interactive: ) -> HostingLogLevel:
hosting_cli.login() if loglevel == constants.LogLevel.DEBUG:
else: return HostingLogLevel.DEBUG
raise SystemExit("Token is required for non-interactive mode.") 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.
@ -519,11 +541,10 @@ 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,
export_fn=lambda zip_dest_dir, export_fn=lambda zip_dest_dir,
api_url, api_url,
deploy_url, deploy_url,
@ -544,10 +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,
**extra, project_name=project_name,
**({"config_path": config_path} if config_path is not None else {}),
) )

View File

@ -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.
@ -1776,9 +1773,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
except Exception as ex: except Exception as ex:
state._clean() state._clean()
app_instance = getattr(prerequisites.get_app(), constants.CompileVars.APP) event_specs = (
prerequisites.get_and_validate_app().app.backend_exception_handler(ex)
event_specs = app_instance.backend_exception_handler(ex) )
if event_specs is None: if event_specs is None:
return StateUpdate() return StateUpdate()
@ -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
@ -1888,9 +1885,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
except Exception as ex: except Exception as ex:
telemetry.send_error(ex, context="backend") telemetry.send_error(ex, context="backend")
app_instance = getattr(prerequisites.get_app(), constants.CompileVars.APP) event_specs = (
prerequisites.get_and_validate_app().app.backend_exception_handler(ex)
event_specs = app_instance.backend_exception_handler(ex) )
yield state._as_state_update( yield state._as_state_update(
handler, handler,
@ -2403,8 +2400,9 @@ class FrontendEventExceptionState(State):
component_stack: The stack trace of the component where the exception occurred. component_stack: The stack trace of the component where the exception occurred.
""" """
app_instance = getattr(prerequisites.get_app(), constants.CompileVars.APP) prerequisites.get_and_validate_app().app.frontend_exception_handler(
app_instance.frontend_exception_handler(Exception(stack)) Exception(stack)
)
class UpdateVarsInternalState(State): class UpdateVarsInternalState(State):
@ -2442,15 +2440,16 @@ class OnLoadInternalState(State):
The list of events to queue for on load handling. The list of events to queue for on load handling.
""" """
# Do not app._compile()! It should be already compiled by now. # Do not app._compile()! It should be already compiled by now.
app = getattr(prerequisites.get_app(), constants.CompileVars.APP) load_events = prerequisites.get_and_validate_app().app.get_load_events(
load_events = app.get_load_events(self.router.page.path) self.router.page.path
)
if not load_events: if not load_events:
self.is_hydrated = True self.is_hydrated = True
return # Fast path for navigation with no on_load events defined. return # Fast path for navigation with no on_load events defined.
self.is_hydrated = False self.is_hydrated = False
return [ return [
*fix_events( *fix_events(
load_events, cast(list[Union[EventSpec, EventHandler]], load_events),
self.router.session.client_token, self.router.session.client_token,
router_data=self.router_data, router_data=self.router_data,
), ),
@ -2612,7 +2611,7 @@ class StateProxy(wrapt.ObjectProxy):
""" """
super().__init__(state_instance) super().__init__(state_instance)
# compile is not relevant to backend logic # compile is not relevant to backend logic
self._self_app = getattr(prerequisites.get_app(), constants.CompileVars.APP) self._self_app = prerequisites.get_and_validate_app().app
self._self_substate_path = tuple(state_instance.get_full_name().split(".")) self._self_substate_path = tuple(state_instance.get_full_name().split("."))
self._self_actx = None self._self_actx = None
self._self_mutable = False self._self_mutable = False
@ -3705,8 +3704,7 @@ def get_state_manager() -> StateManager:
Returns: Returns:
The state manager. The state manager.
""" """
app = getattr(prerequisites.get_app(), constants.CompileVars.APP) return prerequisites.get_and_validate_app().app.state_manager
return app.state_manager
DATACLASS_FIELDS = getattr(dataclasses, "_FIELDS", "__dataclass_fields__") DATACLASS_FIELDS = getattr(dataclasses, "_FIELDS", "__dataclass_fields__")

View File

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

View File

@ -280,6 +280,7 @@ class AppHarness:
before_decorated_pages = reflex.app.DECORATED_PAGES[self.app_name].copy() before_decorated_pages = reflex.app.DECORATED_PAGES[self.app_name].copy()
# Ensure the AppHarness test does not skip State assignment due to running via pytest # Ensure the AppHarness test does not skip State assignment due to running via pytest
os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None) os.environ.pop(reflex.constants.PYTEST_CURRENT_TEST, None)
os.environ[reflex.constants.APP_HARNESS_FLAG] = "true"
self.app_module = reflex.utils.prerequisites.get_compiled_app( self.app_module = reflex.utils.prerequisites.get_compiled_app(
# Do not reload the module for pre-existing apps (only apps generated from source) # Do not reload the module for pre-existing apps (only apps generated from source)
reload=self.app_source is not None reload=self.app_source is not None
@ -934,6 +935,7 @@ class AppHarnessProd(AppHarness):
frontend=True, frontend=True,
backend=False, backend=False,
loglevel=reflex.constants.LogLevel.INFO, loglevel=reflex.constants.LogLevel.INFO,
env=reflex.constants.Env.PROD,
) )
self.frontend_thread = threading.Thread(target=self._run_frontend) self.frontend_thread = threading.Thread(target=self._run_frontend)

View File

@ -13,13 +13,17 @@ from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
from reflex import constants from reflex import constants
from reflex.config import get_config from reflex.config import get_config
from reflex.utils import console, path_ops, prerequisites, processes from reflex.utils import console, path_ops, prerequisites, processes
from reflex.utils.exec import is_in_app_harness
def set_env_json(): def set_env_json():
"""Write the upload url to a REFLEX_JSON.""" """Write the upload url to a REFLEX_JSON."""
path_ops.update_json_file( path_ops.update_json_file(
str(prerequisites.get_web_dir() / constants.Dirs.ENV_JSON), str(prerequisites.get_web_dir() / constants.Dirs.ENV_JSON),
{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint}, {
**{endpoint.name: endpoint.get_url() for endpoint in constants.Endpoint},
"TEST_MODE": is_in_app_harness(),
},
) )

View File

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

View File

@ -1,6 +1,6 @@
"""Custom Exceptions.""" """Custom Exceptions."""
from typing import NoReturn from typing import Any
class ReflexError(Exception): class ReflexError(Exception):
@ -31,6 +31,22 @@ class ComponentTypeError(ReflexError, TypeError):
"""Custom TypeError for component related errors.""" """Custom TypeError for component related errors."""
class ChildrenTypeError(ComponentTypeError):
"""Raised when the children prop of a component is not a valid type."""
def __init__(self, component: str, child: Any):
"""Initialize the exception.
Args:
component: The name of the component.
child: The child that caused the error.
"""
super().__init__(
f"Component {component} received child {child} of type {type(child)}. "
"Accepted types are other components, state vars, or primitive Python types (dict excluded)."
)
class EventHandlerTypeError(ReflexError, TypeError): class EventHandlerTypeError(ReflexError, TypeError):
"""Custom TypeError for event handler related errors.""" """Custom TypeError for event handler related errors."""
@ -59,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."""
@ -95,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."""
@ -170,23 +210,25 @@ 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.
def raise_system_package_missing_error(package: str) -> NoReturn: Args:
"""Raise a SystemPackageMissingError. package: The missing package.
"""
from reflex.constants import IS_MACOS
Args: extra = (
package: The name of the missing system package. 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( class EventDeserializationError(ReflexError, ValueError):
f"System package '{package}' is missing." """Raised when an event cannot be deserialized."""
" 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):

View File

@ -240,6 +240,31 @@ def run_backend(
run_uvicorn_backend(host, port, loglevel) run_uvicorn_backend(host, port, loglevel)
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 = [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:
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 = [module_path]
return reload_dirs
def run_uvicorn_backend(host, port, loglevel: LogLevel): def run_uvicorn_backend(host, port, loglevel: LogLevel):
"""Run the backend in development mode using Uvicorn. """Run the backend in development mode using Uvicorn.
@ -256,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_config().app_name], reload_dirs=list(map(str, get_reload_dirs())),
) )
@ -281,7 +306,7 @@ def run_granian_backend(host, port, loglevel: LogLevel):
interface=Interfaces.ASGI, interface=Interfaces.ASGI,
log_level=LogLevels(loglevel.value), log_level=LogLevels(loglevel.value),
reload=True, reload=True,
reload_paths=[Path(get_config().app_name)], reload_paths=get_reload_dirs(),
reload_ignore_dirs=[".web"], reload_ignore_dirs=[".web"],
).serve() ).serve()
except ImportError: except ImportError:
@ -487,6 +512,15 @@ def is_testing_env() -> bool:
return constants.PYTEST_CURRENT_TEST in os.environ return constants.PYTEST_CURRENT_TEST in os.environ
def is_in_app_harness() -> bool:
"""Whether the app is running in the app harness.
Returns:
True if the app is running in the app harness.
"""
return constants.APP_HARNESS_FLAG in os.environ
def is_prod_mode() -> bool: def is_prod_mode() -> bool:
"""Check if the app is running in production mode. """Check if the app is running in production mode.
@ -495,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()

View File

@ -4,7 +4,7 @@ from pathlib import Path
from typing import Optional from typing import Optional
from reflex import constants from reflex import constants
from reflex.config import get_config from reflex.config import environment, get_config
from reflex.utils import build, console, exec, prerequisites, telemetry from reflex.utils import build, console, exec, prerequisites, telemetry
config = get_config() config = get_config()
@ -18,6 +18,7 @@ def export(
upload_db_file: bool = False, upload_db_file: bool = False,
api_url: Optional[str] = None, api_url: Optional[str] = None,
deploy_url: Optional[str] = None, deploy_url: Optional[str] = None,
env: constants.Env = constants.Env.PROD,
loglevel: constants.LogLevel = console._LOG_LEVEL, loglevel: constants.LogLevel = console._LOG_LEVEL,
): ):
"""Export the app to a zip file. """Export the app to a zip file.
@ -30,11 +31,15 @@ def export(
upload_db_file: Whether to upload the database file. Defaults to False. upload_db_file: Whether to upload the database file. Defaults to False.
api_url: The API URL to use. Defaults to None. api_url: The API URL to use. Defaults to None.
deploy_url: The deploy URL to use. Defaults to None. deploy_url: The deploy URL to use. Defaults to None.
env: The environment to use. Defaults to constants.Env.PROD.
loglevel: The log level to use. Defaults to console._LOG_LEVEL. loglevel: The log level to use. Defaults to console._LOG_LEVEL.
""" """
# Set the log level. # Set the log level.
console.set_log_level(loglevel) console.set_log_level(loglevel)
# Set env mode in the environment
environment.REFLEX_ENV_MODE.set(env)
# Override the config url values if provided. # Override the config url values if provided.
if api_url is not None: if api_url is not None:
config.api_url = str(api_url) config.api_url = str(api_url)

View File

@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, 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
@ -377,37 +376,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,

View File

@ -174,7 +174,7 @@ def get_node_path() -> str | None:
return str(node_path) return str(node_path)
def get_npm_path() -> str | None: def get_npm_path() -> Path | None:
"""Get npm binary path. """Get npm binary path.
Returns: Returns:
@ -183,8 +183,8 @@ def get_npm_path() -> str | None:
npm_path = Path(constants.Node.NPM_PATH) npm_path = Path(constants.Node.NPM_PATH)
if use_system_node() or not npm_path.exists(): if use_system_node() or not npm_path.exists():
system_npm_path = which("npm") system_npm_path = which("npm")
return str(system_npm_path) if system_npm_path else None npm_path = Path(system_npm_path) if system_npm_path else None
return str(npm_path) return npm_path.absolute() if npm_path else None
def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]): def update_json_file(file_path: str | Path, update_dict: dict[str, int | str]):

View File

@ -17,11 +17,12 @@ import stat
import sys import sys
import tempfile import tempfile
import time import time
import typing
import zipfile import zipfile
from datetime import datetime from datetime import datetime
from pathlib import Path from pathlib import Path
from types import ModuleType from types import ModuleType
from typing import Callable, List, Optional from typing import Callable, List, NamedTuple, Optional
import httpx import httpx
import typer import typer
@ -37,14 +38,24 @@ 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
if typing.TYPE_CHECKING:
from reflex.app import App
CURRENTLY_INSTALLING_NODE = False CURRENTLY_INSTALLING_NODE = False
class AppInfo(NamedTuple):
"""A tuple containing the app instance and module."""
app: App
module: ModuleType
@dataclasses.dataclass(frozen=True) @dataclasses.dataclass(frozen=True)
class Template: class Template:
"""A template for a Reflex app.""" """A template for a Reflex app."""
@ -75,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.
@ -109,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
@ -243,7 +240,7 @@ def get_package_manager(on_failure_return_none: bool = False) -> str | None:
""" """
npm_path = path_ops.get_npm_path() npm_path = path_ops.get_npm_path()
if npm_path is not None: if npm_path is not None:
return str(Path(npm_path).resolve()) return str(npm_path)
if on_failure_return_none: if on_failure_return_none:
return None return None
raise FileNotFoundError("NPM not found. You may need to run `reflex init`.") raise FileNotFoundError("NPM not found. You may need to run `reflex init`.")
@ -267,6 +264,22 @@ def windows_npm_escape_hatch() -> bool:
return environment.REFLEX_USE_NPM.get() return environment.REFLEX_USE_NPM.get()
def _check_app_name(config: Config):
"""Check if the app name is set in the config.
Args:
config: The config object.
Raises:
RuntimeError: If the app name is not set in the config.
"""
if not config.app_name:
raise RuntimeError(
"Cannot get the app module because `app_name` is not set in rxconfig! "
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
)
def get_app(reload: bool = False) -> ModuleType: def get_app(reload: bool = False) -> ModuleType:
"""Get the app module based on the default config. """Get the app module based on the default config.
@ -277,22 +290,23 @@ def get_app(reload: bool = False) -> ModuleType:
The app based on the default config. The app based on the default config.
Raises: Raises:
RuntimeError: If the app name is not set in the config. Exception: If an error occurs while getting the app module.
""" """
from reflex.utils import telemetry from reflex.utils import telemetry
try: try:
environment.RELOAD_CONFIG.set(reload) environment.RELOAD_CONFIG.set(reload)
config = get_config() config = get_config()
if not config.app_name:
raise RuntimeError( _check_app_name(config)
"Cannot get the app module because `app_name` is not set in rxconfig! "
"If this error occurs in a reflex test case, ensure that `get_app` is mocked."
)
module = config.module module = config.module
sys.path.insert(0, str(Path.cwd())) sys.path.insert(0, str(Path.cwd()))
app = __import__(module, fromlist=(constants.CompileVars.APP,)) app = (
__import__(module, fromlist=(constants.CompileVars.APP,))
if not config.app_module
else config.app_module
)
if reload: if reload:
from reflex.state import reload_state_module from reflex.state import reload_state_module
@ -301,11 +315,34 @@ def get_app(reload: bool = False) -> ModuleType:
# Reload the app module. # Reload the app module.
importlib.reload(app) importlib.reload(app)
return app
except Exception as ex: except Exception as ex:
telemetry.send_error(ex, context="frontend") telemetry.send_error(ex, context="frontend")
raise raise
else:
return app
def get_and_validate_app(reload: bool = False) -> AppInfo:
"""Get the app instance based on the default config and validate it.
Args:
reload: Re-import the app module from disk
Returns:
The app instance and the app module.
Raises:
RuntimeError: If the app instance is not an instance of rx.App.
"""
from reflex.app import App
app_module = get_app(reload=reload)
app = getattr(app_module, constants.CompileVars.APP)
if not isinstance(app, App):
raise RuntimeError(
"The app instance in the specified app_module_import in rxconfig must be an instance of rx.App."
)
return AppInfo(app=app, module=app_module)
def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType: def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
@ -318,8 +355,7 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
Returns: Returns:
The compiled app based on the default config. The compiled app based on the default config.
""" """
app_module = get_app(reload=reload) app, app_module = get_and_validate_app(reload=reload)
app = getattr(app_module, constants.CompileVars.APP)
# For py3.9 compatibility when redis is used, we MUST add any decorator pages # For py3.9 compatibility when redis is used, we MUST add any decorator pages
# before compiling the app in a thread to avoid event loop error (REF-2172). # before compiling the app in a thread to avoid event loop error (REF-2172).
app._apply_decorated_pages() app._apply_decorated_pages()
@ -835,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):
@ -874,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(
@ -1153,11 +1193,12 @@ def ensure_reflex_installation_id() -> Optional[int]:
if installation_id is None: if installation_id is None:
installation_id = random.getrandbits(128) installation_id = random.getrandbits(128)
installation_id_file.write_text(str(installation_id)) installation_id_file.write_text(str(installation_id))
# If we get here, installation_id is definitely set
return installation_id
except Exception as e: except Exception as e:
console.debug(f"Failed to ensure reflex installation id: {e}") console.debug(f"Failed to ensure reflex installation id: {e}")
return None return None
else:
# If we get here, installation_id is definitely set
return installation_id
def initialize_reflex_user_directory(): def initialize_reflex_user_directory():
@ -1371,19 +1412,22 @@ def create_config_init_app_from_remote_template(app_name: str, template_url: str
except OSError as ose: except OSError as ose:
console.error(f"Failed to create temp directory for extracting zip: {ose}") console.error(f"Failed to create temp directory for extracting zip: {ose}")
raise typer.Exit(1) from ose raise typer.Exit(1) from ose
try: try:
zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir) zipfile.ZipFile(zip_file_path).extractall(path=unzip_dir)
# The zip file downloaded from github looks like: # The zip file downloaded from github looks like:
# repo-name-branch/**/*, so we need to remove the top level directory. # repo-name-branch/**/*, so we need to remove the top level directory.
if len(subdirs := os.listdir(unzip_dir)) != 1:
console.error(f"Expected one directory in the zip, found {subdirs}")
raise typer.Exit(1)
template_dir = unzip_dir / subdirs[0]
console.debug(f"Template folder is located at {template_dir}")
except Exception as uze: except Exception as uze:
console.error(f"Failed to unzip the template: {uze}") console.error(f"Failed to unzip the template: {uze}")
raise typer.Exit(1) from uze raise typer.Exit(1) from uze
if len(subdirs := os.listdir(unzip_dir)) != 1:
console.error(f"Expected one directory in the zip, found {subdirs}")
raise typer.Exit(1)
template_dir = unzip_dir / subdirs[0]
console.debug(f"Template folder is located at {template_dir}")
# Move the rxconfig file here first. # Move the rxconfig file here first.
path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE) path_ops.mv(str(template_dir / constants.Config.FILE), constants.Config.FILE)
new_config = get_config(reload=True) new_config = get_config(reload=True)

View File

@ -9,7 +9,6 @@ import os
import signal import signal
import subprocess import subprocess
from concurrent import futures from concurrent import futures
from pathlib import Path
from typing import Callable, Generator, List, Optional, Tuple, Union from typing import Callable, Generator, List, Optional, Tuple, Union
import psutil import psutil
@ -368,7 +367,7 @@ def get_command_with_loglevel(command: list[str]) -> list[str]:
The updated command list The updated command list
""" """
npm_path = path_ops.get_npm_path() npm_path = path_ops.get_npm_path()
npm_path = str(Path(npm_path).resolve()) if npm_path else npm_path npm_path = str(npm_path) if npm_path else None
if command[0] == npm_path: if command[0] == npm_path:
return [*command, "--loglevel", "silly"] return [*command, "--loglevel", "silly"]

View File

@ -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
@ -156,9 +156,10 @@ def _prepare_event(event: str, **kwargs) -> dict:
def _send_event(event_data: dict) -> bool: def _send_event(event_data: dict) -> bool:
try: try:
httpx.post(POSTHOG_API_URL, json=event_data) httpx.post(POSTHOG_API_URL, json=event_data)
return True
except Exception: except Exception:
return False return False
else:
return True
def _send(event, telemetry_enabled, **kwargs): def _send(event, telemetry_enabled, **kwargs):

View File

@ -855,9 +855,7 @@ def safe_issubclass(cls: Any, class_or_tuple: Any, /) -> bool:
try: try:
return issubclass(cls, class_or_tuple) return issubclass(cls, class_or_tuple)
except TypeError as e: except TypeError as e:
raise TypeError( return False
f"Invalid arguments for issubclass: {cls}, {class_or_tuple}"
) from e
def infallible_issubclass(cls: Any, class_or_tuple: Any, /) -> bool: def infallible_issubclass(cls: Any, class_or_tuple: Any, /) -> bool:

View File

@ -26,6 +26,7 @@ from typing import (
Iterable, Iterable,
List, List,
Literal, Literal,
Mapping,
NoReturn, NoReturn,
Optional, Optional,
Sequence, Sequence,
@ -53,6 +54,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,
@ -74,6 +76,7 @@ from reflex.utils.types import (
has_args, has_args,
infallible_issubclass, infallible_issubclass,
typehint_issubclass, typehint_issubclass,
safe_issubclass,
unionize, unionize,
) )
@ -223,7 +226,7 @@ class VarData:
state: str = "", state: str = "",
field_name: str = "", field_name: str = "",
imports: ImportDict | ParsedImportDict | None = None, imports: ImportDict | ParsedImportDict | None = None,
hooks: dict[str, VarData | None] | None = None, hooks: Mapping[str, VarData | None] | None = None,
components: Iterable[BaseComponent] | None = None, components: Iterable[BaseComponent] | None = None,
deps: list[Var] | None = None, deps: list[Var] | None = None,
position: Hooks.HookPosition | None = None, position: Hooks.HookPosition | None = None,
@ -644,52 +647,21 @@ class Var(Generic[VAR_TYPE]):
def create( def create(
cls, cls,
value: FAKE_VAR_TYPE, value: FAKE_VAR_TYPE,
_var_is_local: bool | None = None,
_var_is_string: bool | None = None,
_var_data: VarData | None = None, _var_data: VarData | None = None,
) -> Var[FAKE_VAR_TYPE]: ) -> Var[FAKE_VAR_TYPE]:
"""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 Var(
_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
@ -747,8 +719,8 @@ class Var(Generic[VAR_TYPE]):
@overload @overload
def to( def to(
self, self,
output: type[dict], output: type[Mapping],
) -> ObjectVar[dict]: ... ) -> ObjectVar[Mapping]: ...
@overload @overload
def to( def to(
@ -952,7 +924,7 @@ class Var(Generic[VAR_TYPE]):
return False return False
if issubclass(type_, list): if issubclass(type_, list):
return [] return []
if issubclass(type_, dict): if issubclass(type_, Mapping):
return {} return {}
if issubclass(type_, tuple): if issubclass(type_, tuple):
return () return ()
@ -1158,7 +1130,7 @@ class Var(Generic[VAR_TYPE]):
f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")] f"$/{constants.Dirs.STATE_PATH}": [imports.ImportVar(tag="refs")]
} }
), ),
).to(ObjectVar, Dict[str, str]) ).to(ObjectVar, Mapping[str, str])
return refs[LiteralVar.create(str(self))] return refs[LiteralVar.create(str(self))]
@deprecated("Use `.js_type()` instead.") @deprecated("Use `.js_type()` instead.")
@ -1551,7 +1523,7 @@ class LiteralVar(Var):
serialized_value = serializers.serialize(value) serialized_value = serializers.serialize(value)
if serialized_value is not None: if serialized_value is not None:
if isinstance(serialized_value, dict): if isinstance(serialized_value, Mapping):
return LiteralObjectVar.create( return LiteralObjectVar.create(
serialized_value, serialized_value,
_var_type=type(value), _var_type=type(value),
@ -1930,8 +1902,8 @@ def figure_out_type(value: Any) -> types.GenericType:
return Set[unionize(*(figure_out_type(v) for v in value))] return Set[unionize(*(figure_out_type(v) for v in value))]
if isinstance(value, tuple): if isinstance(value, tuple):
return Tuple[unionize(*(figure_out_type(v) for v in value)), ...] return Tuple[unionize(*(figure_out_type(v) for v in value)), ...]
if isinstance(value, dict): if isinstance(value, Mapping):
return Dict[ return Mapping[
unionize(*(figure_out_type(k) for k in value)), unionize(*(figure_out_type(k) for k in value)),
unionize(*(figure_out_type(v) for v in value.values())), unionize(*(figure_out_type(v) for v in value.values())),
] ]
@ -2156,19 +2128,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)
@ -2241,6 +2208,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:
@ -2299,10 +2267,10 @@ class ComputedVar(Var[RETURN_TYPE]):
@overload @overload
def __get__( def __get__(
self: ComputedVar[dict[DICT_KEY, DICT_VAL]], self: ComputedVar[Mapping[DICT_KEY, DICT_VAL]],
instance: None, instance: None,
owner: Type, owner: Type,
) -> ObjectVar[dict[DICT_KEY, DICT_VAL]]: ... ) -> ObjectVar[Mapping[DICT_KEY, DICT_VAL]]: ...
@overload @overload
def __get__( def __get__(
@ -2368,12 +2336,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
@ -3229,11 +3194,14 @@ V = TypeVar("V")
BASE_TYPE = TypeVar("BASE_TYPE", bound=Base) BASE_TYPE = TypeVar("BASE_TYPE", bound=Base)
FIELD_TYPE = TypeVar("FIELD_TYPE")
MAPPING_TYPE = TypeVar("MAPPING_TYPE", bound=Mapping)
class Field(Generic[T]):
class Field(Generic[FIELD_TYPE]):
"""Shadow class for Var to allow for type hinting in the IDE.""" """Shadow class for Var to allow for type hinting in the IDE."""
def __set__(self, instance, value: T): def __set__(self, instance, value: FIELD_TYPE):
"""Set the Var. """Set the Var.
Args: Args:
@ -3265,8 +3233,8 @@ class Field(Generic[T]):
@overload @overload
def __get__( def __get__(
self: Field[Dict[str, V]], instance: None, owner self: Field[MAPPING_TYPE], instance: None, owner
) -> ObjectVar[Dict[str, V]]: ... ) -> ObjectVar[MAPPING_TYPE]: ...
@overload @overload
def __get__( def __get__(
@ -3274,10 +3242,10 @@ class Field(Generic[T]):
) -> ObjectVar[BASE_TYPE]: ... ) -> ObjectVar[BASE_TYPE]: ...
@overload @overload
def __get__(self, instance: None, owner) -> Var[T]: ... def __get__(self, instance: None, owner) -> Var[FIELD_TYPE]: ...
@overload @overload
def __get__(self, instance, owner) -> T: ... def __get__(self, instance, owner) -> FIELD_TYPE: ...
def __get__(self, instance, owner): # type: ignore def __get__(self, instance, owner): # type: ignore
"""Get the Var. """Get the Var.
@ -3288,7 +3256,7 @@ class Field(Generic[T]):
""" """
def field(value: T) -> Field[T]: def field(value: FIELD_TYPE) -> Field[FIELD_TYPE]:
"""Create a Field with a value. """Create a Field with a value.
Args: Args:

View File

@ -8,8 +8,8 @@ import typing
from inspect import isclass from inspect import isclass
from typing import ( from typing import (
Any, Any,
Dict,
List, List,
Mapping,
NoReturn, NoReturn,
Sequence, Sequence,
Tuple, Tuple,
@ -20,6 +20,8 @@ from typing import (
overload, overload,
) )
from typing_extensions import is_typeddict
from reflex.utils import types from reflex.utils import types
from reflex.utils.exceptions import VarAttributeError from reflex.utils.exceptions import VarAttributeError
from reflex.utils.types import ( from reflex.utils.types import (
@ -55,7 +57,7 @@ ARRAY_INNER_TYPE = TypeVar("ARRAY_INNER_TYPE")
OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE") OTHER_KEY_TYPE = TypeVar("OTHER_KEY_TYPE")
class ObjectVar(Var[OBJECT_TYPE], python_types=dict): class ObjectVar(Var[OBJECT_TYPE], python_types=Mapping):
"""Base class for immutable object vars.""" """Base class for immutable object vars."""
def _key_type(self) -> Type: def _key_type(self) -> Type:
@ -68,7 +70,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def _value_type( def _value_type(
self: ObjectVar[Dict[Any, VALUE_TYPE]], self: ObjectVar[Mapping[Any, VALUE_TYPE]],
) -> Type[VALUE_TYPE]: ... ) -> Type[VALUE_TYPE]: ...
@overload @overload
@ -83,7 +85,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
fixed_type = get_origin(self._var_type) or self._var_type fixed_type = get_origin(self._var_type) or self._var_type
if not isclass(fixed_type): if not isclass(fixed_type):
return Any return Any
args = get_args(self._var_type) if issubclass(fixed_type, dict) else () args = get_args(self._var_type) if issubclass(fixed_type, Mapping) else ()
return args[1] if args else Any return args[1] if args else Any
def keys(self) -> ArrayVar[Sequence[str]]: def keys(self) -> ArrayVar[Sequence[str]]:
@ -96,7 +98,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def values( def values(
self: ObjectVar[Dict[Any, VALUE_TYPE]], self: ObjectVar[Mapping[Any, VALUE_TYPE]],
) -> ArrayVar[Sequence[VALUE_TYPE]]: ... ) -> ArrayVar[Sequence[VALUE_TYPE]]: ...
@overload @overload
@ -112,7 +114,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
@overload @overload
def entries( def entries(
self: ObjectVar[Dict[Any, VALUE_TYPE]], self: ObjectVar[Mapping[Any, VALUE_TYPE]],
) -> ArrayVar[Sequence[Tuple[str, VALUE_TYPE]]]: ... ) -> ArrayVar[Sequence[Tuple[str, VALUE_TYPE]]]: ...
@overload @overload
@ -142,44 +144,50 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
# NoReturn is used here to catch when key value is Any # NoReturn is used here to catch when key value is Any
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[Any, NoReturn]], self: ObjectVar[Mapping[Any, NoReturn]],
key: Var | Any, key: Var | Any,
) -> Var: ... ) -> Var: ...
@overload
def __getitem__(
self: (ObjectVar[Mapping[Any, bool]]),
key: Var | Any,
) -> BooleanVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ( self: (
ObjectVar[Dict[Any, int]] ObjectVar[Mapping[Any, int]]
| ObjectVar[Dict[Any, float]] | ObjectVar[Mapping[Any, float]]
| ObjectVar[Dict[Any, int | float]] | ObjectVar[Mapping[Any, int | float]]
), ),
key: Var | Any, key: Var | Any,
) -> NumberVar: ... ) -> NumberVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[Any, str]], self: ObjectVar[Mapping[Any, str]],
key: Var | Any, key: Var | Any,
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]] self: ObjectVar[Mapping[Any, Sequence[ARRAY_INNER_TYPE]]]
| ObjectVar[Dict[Any, List[ARRAY_INNER_TYPE]]], | ObjectVar[Mapping[Any, List[ARRAY_INNER_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[Any, set[ARRAY_INNER_TYPE]]], self: ObjectVar[Mapping[Any, set[ARRAY_INNER_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getitem__( def __getitem__(
self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], self: ObjectVar[Mapping[Any, Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]],
key: Var | Any, key: Var | Any,
) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ... ) -> ObjectVar[Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
def __getitem__(self, key: Var | Any) -> Var: def __getitem__(self, key: Var | Any) -> Var:
"""Get an item from the object. """Get an item from the object.
@ -199,43 +207,43 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
# NoReturn is used here to catch when key value is Any # NoReturn is used here to catch when key value is Any
@overload @overload
def __getattr__( # pyright: ignore [reportOverlappingOverload] def __getattr__( # pyright: ignore [reportOverlappingOverload]
self: ObjectVar[Dict[Any, NoReturn]], self: ObjectVar[Mapping[Any, NoReturn]],
name: str, name: str,
) -> Var: ... ) -> Var: ...
@overload @overload
def __getattr__( def __getattr__(
self: ( self: (
ObjectVar[Dict[Any, int]] ObjectVar[Mapping[Any, int]]
| ObjectVar[Dict[Any, float]] | ObjectVar[Mapping[Any, float]]
| ObjectVar[Dict[Any, int | float]] | ObjectVar[Mapping[Any, int | float]]
), ),
name: str, name: str,
) -> NumberVar: ... ) -> NumberVar: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[Any, str]], self: ObjectVar[Mapping[Any, str]],
name: str, name: str,
) -> StringVar: ... ) -> StringVar: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[Any, Sequence[ARRAY_INNER_TYPE]]], self: ObjectVar[Mapping[Any, Sequence[ARRAY_INNER_TYPE]]],
name: str, name: str,
) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[Sequence[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[Any, set[ARRAY_INNER_TYPE]]], self: ObjectVar[Mapping[Any, set[ARRAY_INNER_TYPE]]],
name: str, name: str,
) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ... ) -> ArrayVar[set[ARRAY_INNER_TYPE]]: ...
@overload @overload
def __getattr__( def __getattr__(
self: ObjectVar[Dict[Any, dict[OTHER_KEY_TYPE, VALUE_TYPE]]], self: ObjectVar[Mapping[Any, Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]],
name: str, name: str,
) -> ObjectVar[dict[OTHER_KEY_TYPE, VALUE_TYPE]]: ... ) -> ObjectVar[Mapping[OTHER_KEY_TYPE, VALUE_TYPE]]: ...
@overload @overload
def __getattr__( def __getattr__(
@ -264,8 +272,11 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
var_type = get_args(var_type)[0] var_type = get_args(var_type)[0]
fixed_type = var_type if isclass(var_type) else get_origin(var_type) fixed_type = var_type if isclass(var_type) else get_origin(var_type)
if (isclass(fixed_type) and not issubclass(fixed_type, dict)) or (
fixed_type in types.UnionTypes if (
(isclass(fixed_type) and not issubclass(fixed_type, Mapping))
or (fixed_type in types.UnionTypes)
or is_typeddict(fixed_type)
): ):
attribute_type = get_attribute_access_type(var_type, name) attribute_type = get_attribute_access_type(var_type, name)
if attribute_type is None: if attribute_type is None:
@ -297,7 +308,7 @@ class ObjectVar(Var[OBJECT_TYPE], python_types=dict):
class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar): class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar):
"""Base class for immutable literal object vars.""" """Base class for immutable literal object vars."""
_var_value: Dict[Union[Var, Any], Union[Var, Any]] = dataclasses.field( _var_value: Mapping[Union[Var, Any], Union[Var, Any]] = dataclasses.field(
default_factory=dict default_factory=dict
) )
@ -381,7 +392,7 @@ class LiteralObjectVar(CachedVarOperation, ObjectVar[OBJECT_TYPE], LiteralVar):
@classmethod @classmethod
def create( def create(
cls, cls,
_var_value: dict, _var_value: Mapping,
_var_type: Type[OBJECT_TYPE] | None = None, _var_type: Type[OBJECT_TYPE] | None = None,
_var_data: VarData | None = None, _var_data: VarData | None = None,
) -> LiteralObjectVar[OBJECT_TYPE]: ) -> LiteralObjectVar[OBJECT_TYPE]:
@ -477,14 +488,14 @@ def object_merge_operation(lhs: Var, rhs: Var):
return var_operation_return( return var_operation_return(
js_expression=f"({{...{lhs}, ...{rhs}}})", js_expression=f"({{...{lhs}, ...{rhs}}})",
type_computer=nary_type_computer( type_computer=nary_type_computer(
ReflexCallable[[Any, Any], Dict[Any, Any]], ReflexCallable[[Any, Any], Mapping[Any, Any]],
ReflexCallable[[Any], Dict[Any, Any]], ReflexCallable[[Any], Mapping[Any, Any]],
computer=lambda args: Dict[ computer=lambda args: Mapping[
unionize(*[arg.to(ObjectVar)._key_type() for arg in args]), unionize(*[arg.to(ObjectVar)._key_type() for arg in args]),
unionize(*[arg.to(ObjectVar)._value_type() for arg in args]), unionize(*[arg.to(ObjectVar)._value_type() for arg in args]),
], ],
), ),
var_type=Dict[Any, Any], var_type=Mapping[Any, Any],
) )

View File

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

View File

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

View File

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

View File

@ -71,9 +71,10 @@ def has_error_modal(driver: WebDriver) -> bool:
""" """
try: try:
driver.find_element(By.XPATH, CONNECTION_ERROR_XPATH) driver.find_element(By.XPATH, CONNECTION_ERROR_XPATH)
return True
except NoSuchElementException: except NoSuchElementException:
return False return False
else:
return True
@pytest.mark.asyncio @pytest.mark.asyncio

View File

@ -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",
[ [

View File

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

View File

@ -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
@ -76,17 +77,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,
), ),
], ],
@ -100,7 +101,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:

View File

@ -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 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.
@ -645,14 +653,17 @@ def test_create_filters_none_props(test_component):
assert str(component.style["text-align"]) == '"center"' assert str(component.style["text-align"]) == '"center"'
@pytest.mark.parametrize("children", [((None,),), ("foo", ("bar", (None,)))]) @pytest.mark.parametrize(
"children",
[
((None,),),
("foo", ("bar", (None,))),
({"foo": "bar"},),
],
)
def test_component_create_unallowed_types(children, test_component): def test_component_create_unallowed_types(children, test_component):
with pytest.raises(TypeError) as err: with pytest.raises(ChildrenTypeError):
test_component.create(*children) test_component.create(*children)
assert (
err.value.args[0]
== "Children of Reflex components must be other components, state vars, or primitive Python types. Got child None of type <class 'NoneType'>."
)
@pytest.mark.parametrize( @pytest.mark.parametrize(
@ -815,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
@ -826,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):
@ -842,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"] : (((_lhs, _rhs) => (_lhs + _rhs))(_charlie["custom"], 42)) }}), ({{ }})))], '
"[__e, _alpha, _bravo, _charlie], ({ }))))}"
)
def test_create_custom_component(my_component): def test_create_custom_component(my_component):
@ -905,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):
@ -941,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),
@ -1795,21 +1805,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()

View File

@ -200,16 +200,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

View File

@ -1148,7 +1148,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()
@ -1426,7 +1426,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:
@ -1435,7 +1435,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:
@ -1448,7 +1448,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:
@ -3140,7 +3140,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):

View File

@ -1,6 +1,6 @@
from __future__ import annotations from __future__ import annotations
from typing import Any, Dict from typing import Any, Mapping
import pytest import pytest
@ -379,7 +379,7 @@ class StyleState(rx.State):
{ {
"css": Var( "css": Var(
_js_expr=f'({{ ["color"] : ("dark"+{StyleState.color}) }})' _js_expr=f'({{ ["color"] : ("dark"+{StyleState.color}) }})'
).to(Dict[str, str]) ).to(Mapping[str, str])
}, },
), ),
( (

View File

@ -2,7 +2,7 @@ import json
import math import math
import sys import sys
import typing import typing
from typing import Dict, List, Optional, Set, Tuple, Union, cast from typing import Dict, List, Mapping, Optional, Set, Tuple, Union, cast
import pytest import pytest
from pandas import DataFrame from pandas import DataFrame
@ -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 (
@ -270,7 +273,7 @@ def test_get_setter(prop: Var, expected):
([1, 2, 3], Var(_js_expr="[1, 2, 3]", _var_type=List[int])), ([1, 2, 3], Var(_js_expr="[1, 2, 3]", _var_type=List[int])),
( (
{"a": 1, "b": 2}, {"a": 1, "b": 2},
Var(_js_expr='({ ["a"] : 1, ["b"] : 2 })', _var_type=Dict[str, int]), Var(_js_expr='({ ["a"] : 1, ["b"] : 2 })', _var_type=Mapping[str, int]),
), ),
], ],
) )
@ -943,7 +946,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

View File

@ -1,4 +1,4 @@
from typing import Dict, List, Union from typing import List, Mapping, Union
import pytest import pytest
@ -37,12 +37,12 @@ class ChildGenericDict(GenericDict):
("a", str), ("a", str),
([1, 2, 3], List[int]), ([1, 2, 3], List[int]),
([1, 2.0, "a"], List[Union[int, float, str]]), ([1, 2.0, "a"], List[Union[int, float, str]]),
({"a": 1, "b": 2}, Dict[str, int]), ({"a": 1, "b": 2}, Mapping[str, int]),
({"a": 1, 2: "b"}, Dict[Union[int, str], Union[str, int]]), ({"a": 1, 2: "b"}, Mapping[Union[int, str], Union[str, int]]),
(CustomDict(), CustomDict), (CustomDict(), CustomDict),
(ChildCustomDict(), ChildCustomDict), (ChildCustomDict(), ChildCustomDict),
(GenericDict({1: 1}), Dict[int, int]), (GenericDict({1: 1}), Mapping[int, int]),
(ChildGenericDict({1: 1}), Dict[int, int]), (ChildGenericDict({1: 1}), Mapping[int, int]),
], ],
) )
def test_figure_out_type(value, expected): def test_figure_out_type(value, expected):