This commit is contained in:
Khaleel Al-Adhami 2024-10-02 12:11:43 -07:00
commit 8b74a7a1e9
189 changed files with 2345 additions and 835 deletions

View File

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

40
.github/workflows/check_node_latest.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: integration-node-latest
on:
push:
branches:
- main
pull_request:
branches:
- main
env:
TELEMETRY_ENABLED: false
REFLEX_USE_SYSTEM_NODE: true
jobs:
check_latest_node:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.12']
node-version: ['node']
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/setup_build_env
with:
python-version: ${{ matrix.python-version }}
run-poetry-install: true
create-venv-at-path: .venv
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: |
poetry run uv pip install pyvirtualdisplay pillow
poetry run playwright install --with-deps
- run: |
poetry run pytest tests/test_node_version.py
poetry run pytest tests/integration

View File

@ -51,7 +51,7 @@ jobs:
SCREENSHOT_DIR: /tmp/screenshots
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
run: |
poetry run pytest integration
poetry run pytest tests/integration
- uses: actions/upload-artifact@v4
name: Upload failed test screenshots
if: always()

View File

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

View File

@ -28,5 +28,5 @@ jobs:
# Run reflex init in a docker container
# cwd is repo root
docker build -f integration/init-test/Dockerfile -t reflex-init-test integration/init-test
docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/integration/init-test/in_docker_test_script.sh
docker build -f tests/integration/init-test/Dockerfile -t reflex-init-test tests/integration/init-test
docker run --rm -v $(pwd):/reflex-repo/ reflex-init-test /reflex-repo/tests/integration/init-test/in_docker_test_script.sh

View File

@ -28,14 +28,18 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-12]
python-version: ['3.10.13', '3.11.5', '3.12.0']
python-version: ['3.9.18', '3.10.13', '3.11.5', '3.12.0']
# Windows is a bit behind on Python version availability in Github
exclude:
- os: windows-latest
python-version: '3.10.13'
- os: windows-latest
python-version: '3.9.18'
include:
- os: windows-latest
python-version: '3.10.11'
- os: windows-latest
python-version: '3.9.13'
runs-on: ${{ matrix.os }}
# Service containers to run with `runner-job`
services:
@ -61,17 +65,17 @@ jobs:
- name: Run unit tests
run: |
export PYTHONUNBUFFERED=1
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
- name: Run unit tests w/ redis
if: ${{ matrix.os == 'ubuntu-latest' }}
run: |
export PYTHONUNBUFFERED=1
export REDIS_URL=redis://localhost:6379
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
# Change to explicitly install v1 when reflex-hosting-cli is compatible with v2
- name: Run unit tests w/ pydantic v1
run: |
export PYTHONUNBUFFERED=1
poetry run uv pip install "pydantic~=1.10"
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
- run: poetry run coverage html

View File

@ -6,7 +6,7 @@ repos:
rev: v0.4.10
hooks:
- id: ruff-format
args: [integration, reflex, tests]
args: [reflex, tests]
- id: ruff
args: ["--fix", "--exit-non-zero-on-fix"]
exclude: '^integration/benchmarks/'

View File

@ -8,7 +8,7 @@ Here is a quick guide on how to run Reflex repo locally so you can start contrib
**Prerequisites:**
- Python >= 3.10
- Python >= 3.9
- 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:**
@ -69,7 +69,7 @@ In your `reflex` directory run make sure all the unit tests are still passing us
This will fail if code coverage is below 70%.
``` bash
poetry run pytest tests --cov --no-cov-on-fail --cov-report=
poetry run pytest tests/units --cov --no-cov-on-fail --cov-report=
```
Next make sure all the following tests pass. This ensures that every new change has proper documentation and type checking.
@ -87,7 +87,7 @@ poetry run ruff format .
```
Consider installing git pre-commit hooks so Ruff, Pyright, Darglint and `make_pyi` will run automatically before each commit.
Note that pre-commit will only be installed when you use a Python version >= 3.8.
Note that pre-commit will only be installed when you use a Python version >= 3.9.
``` bash
pre-commit install

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

581
poetry.lock generated
View File

@ -2,13 +2,13 @@
[[package]]
name = "alembic"
version = "1.13.2"
version = "1.13.3"
description = "A database migration tool for SQLAlchemy."
optional = false
python-versions = ">=3.8"
files = [
{file = "alembic-1.13.2-py3-none-any.whl", hash = "sha256:6b8733129a6224a9a711e17c99b08462dbf7cc9670ba8f2e2ae9af860ceb1953"},
{file = "alembic-1.13.2.tar.gz", hash = "sha256:1ff0ae32975f4fd96028c39ed9bb3c867fe3af956bd7bb37343b54c9fe7445ef"},
{file = "alembic-1.13.3-py3-none-any.whl", hash = "sha256:908e905976d15235fae59c9ac42c4c5b75cfcefe3d27c0fbf7ae15a37715d80e"},
{file = "alembic-1.13.3.tar.gz", hash = "sha256:203503117415561e203aa14541740643a611f641517f0209fcae63e9fa09f1a2"},
]
[package.dependencies]
@ -32,13 +32,13 @@ files = [
[[package]]
name = "anyio"
version = "4.5.0"
version = "4.6.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "anyio-4.5.0-py3-none-any.whl", hash = "sha256:fdeb095b7cc5a5563175eedd926ec4ae55413bb4be5770c424af0ba46ccb4a78"},
{file = "anyio-4.5.0.tar.gz", hash = "sha256:c5a275fe5ca0afd788001f58fca1e69e29ce706d746e317d660e21f70c530ef9"},
{file = "anyio-4.6.0-py3-none-any.whl", hash = "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a"},
{file = "anyio-4.6.0.tar.gz", hash = "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb"},
]
[package.dependencies]
@ -616,77 +616,69 @@ typing = ["typing-extensions (>=4.12.2)"]
[[package]]
name = "greenlet"
version = "3.1.0"
version = "3.0.3"
description = "Lightweight in-process concurrent programming"
optional = false
python-versions = ">=3.7"
files = [
{file = "greenlet-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a814dc3100e8a046ff48faeaa909e80cdb358411a3d6dd5293158425c684eda8"},
{file = "greenlet-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a771dc64fa44ebe58d65768d869fcfb9060169d203446c1d446e844b62bdfdca"},
{file = "greenlet-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0e49a65d25d7350cca2da15aac31b6f67a43d867448babf997fe83c7505f57bc"},
{file = "greenlet-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cd8518eade968bc52262d8c46727cfc0826ff4d552cf0430b8d65aaf50bb91d"},
{file = "greenlet-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76dc19e660baea5c38e949455c1181bc018893f25372d10ffe24b3ed7341fb25"},
{file = "greenlet-3.1.0-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c0a5b1c22c82831f56f2f7ad9bbe4948879762fe0d59833a4a71f16e5fa0f682"},
{file = "greenlet-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:2651dfb006f391bcb240635079a68a261b227a10a08af6349cba834a2141efa1"},
{file = "greenlet-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3e7e6ef1737a819819b1163116ad4b48d06cfdd40352d813bb14436024fcda99"},
{file = "greenlet-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:ffb08f2a1e59d38c7b8b9ac8083c9c8b9875f0955b1e9b9b9a965607a51f8e54"},
{file = "greenlet-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9730929375021ec90f6447bff4f7f5508faef1c02f399a1953870cdb78e0c345"},
{file = "greenlet-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:713d450cf8e61854de9420fb7eea8ad228df4e27e7d4ed465de98c955d2b3fa6"},
{file = "greenlet-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c3446937be153718250fe421da548f973124189f18fe4575a0510b5c928f0cc"},
{file = "greenlet-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1ddc7bcedeb47187be74208bc652d63d6b20cb24f4e596bd356092d8000da6d6"},
{file = "greenlet-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44151d7b81b9391ed759a2f2865bbe623ef00d648fed59363be2bbbd5154656f"},
{file = "greenlet-3.1.0-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6cea1cca3be76c9483282dc7760ea1cc08a6ecec1f0b6ca0a94ea0d17432da19"},
{file = "greenlet-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:619935a44f414274a2c08c9e74611965650b730eb4efe4b2270f91df5e4adf9a"},
{file = "greenlet-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:221169d31cada333a0c7fd087b957c8f431c1dba202c3a58cf5a3583ed973e9b"},
{file = "greenlet-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:01059afb9b178606b4b6e92c3e710ea1635597c3537e44da69f4531e111dd5e9"},
{file = "greenlet-3.1.0-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:24fc216ec7c8be9becba8b64a98a78f9cd057fd2dc75ae952ca94ed8a893bf27"},
{file = "greenlet-3.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d07c28b85b350564bdff9f51c1c5007dfb2f389385d1bc23288de51134ca303"},
{file = "greenlet-3.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:243a223c96a4246f8a30ea470c440fe9db1f5e444941ee3c3cd79df119b8eebf"},
{file = "greenlet-3.1.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:26811df4dc81271033a7836bc20d12cd30938e6bd2e9437f56fa03da81b0f8fc"},
{file = "greenlet-3.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9d86401550b09a55410f32ceb5fe7efcd998bd2dad9e82521713cb148a4a15f"},
{file = "greenlet-3.1.0-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:26d9c1c4f1748ccac0bae1dbb465fb1a795a75aba8af8ca871503019f4285e2a"},
{file = "greenlet-3.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:cd468ec62257bb4544989402b19d795d2305eccb06cde5da0eb739b63dc04665"},
{file = "greenlet-3.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a53dfe8f82b715319e9953330fa5c8708b610d48b5c59f1316337302af5c0811"},
{file = "greenlet-3.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:28fe80a3eb673b2d5cc3b12eea468a5e5f4603c26aa34d88bf61bba82ceb2f9b"},
{file = "greenlet-3.1.0-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:76b3e3976d2a452cba7aa9e453498ac72240d43030fdc6d538a72b87eaff52fd"},
{file = "greenlet-3.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:655b21ffd37a96b1e78cc48bf254f5ea4b5b85efaf9e9e2a526b3c9309d660ca"},
{file = "greenlet-3.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c6f4c2027689093775fd58ca2388d58789009116844432d920e9147f91acbe64"},
{file = "greenlet-3.1.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:76e5064fd8e94c3f74d9fd69b02d99e3cdb8fc286ed49a1f10b256e59d0d3a0b"},
{file = "greenlet-3.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a4bf607f690f7987ab3291406e012cd8591a4f77aa54f29b890f9c331e84989"},
{file = "greenlet-3.1.0-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:037d9ac99540ace9424cb9ea89f0accfaff4316f149520b4ae293eebc5bded17"},
{file = "greenlet-3.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:90b5bbf05fe3d3ef697103850c2ce3374558f6fe40fd57c9fac1bf14903f50a5"},
{file = "greenlet-3.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:726377bd60081172685c0ff46afbc600d064f01053190e4450857483c4d44484"},
{file = "greenlet-3.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:d46d5069e2eeda111d6f71970e341f4bd9aeeee92074e649ae263b834286ecc0"},
{file = "greenlet-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81eeec4403a7d7684b5812a8aaa626fa23b7d0848edb3a28d2eb3220daddcbd0"},
{file = "greenlet-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4a3dae7492d16e85ea6045fd11cb8e782b63eac8c8d520c3a92c02ac4573b0a6"},
{file = "greenlet-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b5ea3664eed571779403858d7cd0a9b0ebf50d57d2cdeafc7748e09ef8cd81a"},
{file = "greenlet-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22f4e26400f7f48faef2d69c20dc055a1f3043d330923f9abe08ea0aecc44df"},
{file = "greenlet-3.1.0-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13ff8c8e54a10472ce3b2a2da007f915175192f18e6495bad50486e87c7f6637"},
{file = "greenlet-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f9671e7282d8c6fcabc32c0fb8d7c0ea8894ae85cee89c9aadc2d7129e1a9954"},
{file = "greenlet-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:184258372ae9e1e9bddce6f187967f2e08ecd16906557c4320e3ba88a93438c3"},
{file = "greenlet-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:a0409bc18a9f85321399c29baf93545152d74a49d92f2f55302f122007cfda00"},
{file = "greenlet-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:9eb4a1d7399b9f3c7ac68ae6baa6be5f9195d1d08c9ddc45ad559aa6b556bce6"},
{file = "greenlet-3.1.0-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:a8870983af660798dc1b529e1fd6f1cefd94e45135a32e58bd70edd694540f33"},
{file = "greenlet-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cfcfb73aed40f550a57ea904629bdaf2e562c68fa1164fa4588e752af6efdc3f"},
{file = "greenlet-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f9482c2ed414781c0af0b35d9d575226da6b728bd1a720668fa05837184965b7"},
{file = "greenlet-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d58ec349e0c2c0bc6669bf2cd4982d2f93bf067860d23a0ea1fe677b0f0b1e09"},
{file = "greenlet-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd65695a8df1233309b701dec2539cc4b11e97d4fcc0f4185b4a12ce54db0491"},
{file = "greenlet-3.1.0-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:665b21e95bc0fce5cab03b2e1d90ba9c66c510f1bb5fdc864f3a377d0f553f6b"},
{file = "greenlet-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3c59a06c2c28a81a026ff11fbf012081ea34fb9b7052f2ed0366e14896f0a1d"},
{file = "greenlet-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415b9494ff6240b09af06b91a375731febe0090218e2898d2b85f9b92abcda0"},
{file = "greenlet-3.1.0-cp38-cp38-win32.whl", hash = "sha256:1544b8dd090b494c55e60c4ff46e238be44fdc472d2589e943c241e0169bcea2"},
{file = "greenlet-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:7f346d24d74c00b6730440f5eb8ec3fe5774ca8d1c9574e8e57c8671bb51b910"},
{file = "greenlet-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:db1b3ccb93488328c74e97ff888604a8b95ae4f35f4f56677ca57a4fc3a4220b"},
{file = "greenlet-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:44cd313629ded43bb3b98737bba2f3e2c2c8679b55ea29ed73daea6b755fe8e7"},
{file = "greenlet-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fad7a051e07f64e297e6e8399b4d6a3bdcad3d7297409e9a06ef8cbccff4f501"},
{file = "greenlet-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3967dcc1cd2ea61b08b0b276659242cbce5caca39e7cbc02408222fb9e6ff39"},
{file = "greenlet-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d45b75b0f3fd8d99f62eb7908cfa6d727b7ed190737dec7fe46d993da550b81a"},
{file = "greenlet-3.1.0-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d004db911ed7b6218ec5c5bfe4cf70ae8aa2223dffbb5b3c69e342bb253cb28"},
{file = "greenlet-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b9505a0c8579899057cbefd4ec34d865ab99852baf1ff33a9481eb3924e2da0b"},
{file = "greenlet-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fd6e94593f6f9714dbad1aaba734b5ec04593374fa6638df61592055868f8b8"},
{file = "greenlet-3.1.0-cp39-cp39-win32.whl", hash = "sha256:d0dd943282231480aad5f50f89bdf26690c995e8ff555f26d8a5b9887b559bcc"},
{file = "greenlet-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:ac0adfdb3a21dc2a24ed728b61e72440d297d0fd3a577389df566651fcd08f97"},
{file = "greenlet-3.1.0.tar.gz", hash = "sha256:b395121e9bbe8d02a750886f108d540abe66075e61e22f7353d9acb0b81be0f0"},
{file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"},
{file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"},
{file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"},
{file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"},
{file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"},
{file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"},
{file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"},
{file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"},
{file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"},
{file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"},
{file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"},
{file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"},
{file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"},
{file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"},
{file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"},
{file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"},
{file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"},
{file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"},
{file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"},
{file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"},
{file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"},
{file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"},
{file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"},
{file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"},
{file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"},
{file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"},
{file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"},
{file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"},
{file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"},
{file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"},
{file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"},
{file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"},
]
[package.extras]
@ -921,13 +913,13 @@ i18n = ["Babel (>=2.7)"]
[[package]]
name = "keyring"
version = "25.4.0"
version = "25.4.1"
description = "Store and access your passwords safely."
optional = false
python-versions = ">=3.8"
files = [
{file = "keyring-25.4.0-py3-none-any.whl", hash = "sha256:a2a630d5c9bef5d3f0968d15ef4e42b894a83e17494edcb67b154c36491c9920"},
{file = "keyring-25.4.0.tar.gz", hash = "sha256:ae8263fd9264c94f91ad82d098f8a5bb1b7fa71ce0a72388dc4fc0be3f6a034e"},
{file = "keyring-25.4.1-py3-none-any.whl", hash = "sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf"},
{file = "keyring-25.4.1.tar.gz", hash = "sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b"},
]
[package.dependencies]
@ -1137,6 +1129,60 @@ files = [
{file = "nodeenv-1.9.1.tar.gz", hash = "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f"},
]
[[package]]
name = "numpy"
version = "2.0.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.1.1"
@ -1226,40 +1272,53 @@ files = [
[[package]]
name = "pandas"
version = "2.2.2"
version = "2.2.3"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.9"
files = [
{file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"},
{file = "pandas-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7adfc142dac335d8c1e0dcbd37eb8617eac386596eb9e1a1b77791cf2498238"},
{file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"},
{file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"},
{file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"},
{file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"},
{file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"},
{file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"},
{file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"},
{file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"},
{file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"},
{file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"},
{file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"},
{file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"},
{file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"},
{file = "pandas-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e9b79011ff7a0f4b1d6da6a61aa1aa604fb312d6647de5bad20013682d1429ce"},
{file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"},
{file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"},
{file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"},
{file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"},
{file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"},
{file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"},
{file = "pandas-2.2.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9057e6aa78a584bc93a13f0a9bf7e753a5e9770a30b4d758b8d5f2a62a9433cd"},
{file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"},
{file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"},
{file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"},
{file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"},
{file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"},
{file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"},
{file = "pandas-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5"},
{file = "pandas-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348"},
{file = "pandas-2.2.3-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed"},
{file = "pandas-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57"},
{file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42"},
{file = "pandas-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f"},
{file = "pandas-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645"},
{file = "pandas-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039"},
{file = "pandas-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd"},
{file = "pandas-2.2.3-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698"},
{file = "pandas-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc"},
{file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3"},
{file = "pandas-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32"},
{file = "pandas-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5"},
{file = "pandas-2.2.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9"},
{file = "pandas-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4"},
{file = "pandas-2.2.3-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3"},
{file = "pandas-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319"},
{file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8"},
{file = "pandas-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a"},
{file = "pandas-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13"},
{file = "pandas-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015"},
{file = "pandas-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28"},
{file = "pandas-2.2.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0"},
{file = "pandas-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24"},
{file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659"},
{file = "pandas-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb"},
{file = "pandas-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d"},
{file = "pandas-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468"},
{file = "pandas-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18"},
{file = "pandas-2.2.3-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2"},
{file = "pandas-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4"},
{file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d"},
{file = "pandas-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a"},
{file = "pandas-2.2.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39"},
{file = "pandas-2.2.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30"},
{file = "pandas-2.2.3-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c"},
{file = "pandas-2.2.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c"},
{file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea"},
{file = "pandas-2.2.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761"},
{file = "pandas-2.2.3-cp39-cp39-win_amd64.whl", hash = "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e"},
{file = "pandas-2.2.3.tar.gz", hash = "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667"},
]
[package.dependencies]
@ -1453,6 +1512,26 @@ docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-a
test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"]
type = ["mypy (>=1.11.2)"]
[[package]]
name = "playwright"
version = "1.47.0"
description = "A high-level API to automate web browsers"
optional = false
python-versions = ">=3.8"
files = [
{file = "playwright-1.47.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:f205df24edb925db1a4ab62f1ab0da06f14bb69e382efecfb0deedc4c7f4b8cd"},
{file = "playwright-1.47.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7fc820faf6885f69a52ba4ec94124e575d3c4a4003bf29200029b4a4f2b2d0ab"},
{file = "playwright-1.47.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:8e212dc472ff19c7d46ed7e900191c7a786ce697556ac3f1615986ec3aa00341"},
{file = "playwright-1.47.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:a1935672531963e4b2a321de5aa59b982fb92463ee6e1032dd7326378e462955"},
{file = "playwright-1.47.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0a1b61473d6f7f39c5d77d4800b3cbefecb03344c90b98f3fbcae63294ad249"},
{file = "playwright-1.47.0-py3-none-win32.whl", hash = "sha256:1b977ed81f6bba5582617684a21adab9bad5676d90a357ebf892db7bdf4a9974"},
{file = "playwright-1.47.0-py3-none-win_amd64.whl", hash = "sha256:0ec1056042d2e86088795a503347407570bffa32cbe20748e5d4c93dba085280"},
]
[package.dependencies]
greenlet = "3.0.3"
pyee = "12.0.0"
[[package]]
name = "plotly"
version = "5.24.1"
@ -1676,6 +1755,23 @@ files = [
[package.dependencies]
typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0"
[[package]]
name = "pyee"
version = "12.0.0"
description = "A rough port of Node.js's EventEmitter to Python with a few tricks of its own"
optional = false
python-versions = ">=3.8"
files = [
{file = "pyee-12.0.0-py3-none-any.whl", hash = "sha256:7b14b74320600049ccc7d0e0b1becd3b4bd0a03c745758225e31a59f4095c990"},
{file = "pyee-12.0.0.tar.gz", hash = "sha256:c480603f4aa2927d4766eb41fa82793fe60a82cbfdb8d688e0d08c55a534e145"},
]
[package.dependencies]
typing-extensions = "*"
[package.extras]
dev = ["black", "build", "flake8", "flake8-black", "isort", "jupyter-console", "mkdocs", "mkdocs-include-markdown-plugin", "mkdocstrings[python]", "pytest", "pytest-asyncio", "pytest-trio", "sphinx", "toml", "tox", "trio", "trio", "trio-typing", "twine", "twisted", "validate-pyproject[all]"]
[[package]]
name = "pygments"
version = "2.18.0"
@ -1771,6 +1867,24 @@ pytest = ">=7.0.0"
docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"]
testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"]
[[package]]
name = "pytest-base-url"
version = "2.1.0"
description = "pytest plugin for URL based testing"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest_base_url-2.1.0-py3-none-any.whl", hash = "sha256:3ad15611778764d451927b2a53240c1a7a591b521ea44cebfe45849d2d2812e6"},
{file = "pytest_base_url-2.1.0.tar.gz", hash = "sha256:02748589a54f9e63fcbe62301d6b0496da0d10231b753e950c63e03aee745d45"},
]
[package.dependencies]
pytest = ">=7.0.0"
requests = ">=2.9"
[package.extras]
test = ["black (>=22.1.0)", "flake8 (>=4.0.1)", "pre-commit (>=2.17.0)", "pytest-localserver (>=0.7.1)", "tox (>=3.24.5)"]
[[package]]
name = "pytest-benchmark"
version = "4.0.0"
@ -1826,6 +1940,23 @@ pytest = ">=6.2.5"
[package.extras]
dev = ["pre-commit", "pytest-asyncio", "tox"]
[[package]]
name = "pytest-playwright"
version = "0.5.2"
description = "A pytest wrapper with fixtures for Playwright to automate web browsers"
optional = false
python-versions = ">=3.8"
files = [
{file = "pytest_playwright-0.5.2-py3-none-any.whl", hash = "sha256:2c5720591364a1cdf66610b972ff8492512bc380953e043c85f705b78b2ed582"},
{file = "pytest_playwright-0.5.2.tar.gz", hash = "sha256:c6d603df9e6c50b35f057b0528e11d41c0963283e98c257267117f5ed6ba1924"},
]
[package.dependencies]
playwright = ">=1.18"
pytest = ">=6.2.4,<9.0.0"
pytest-base-url = ">=1.0.0,<3.0.0"
python-slugify = ">=6.0.0,<9.0.0"
[[package]]
name = "python-dateutil"
version = "2.9.0.post0"
@ -1861,17 +1992,31 @@ docs = ["sphinx"]
[[package]]
name = "python-multipart"
version = "0.0.9"
version = "0.0.10"
description = "A streaming multipart parser for Python"
optional = false
python-versions = ">=3.8"
files = [
{file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
{file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
{file = "python_multipart-0.0.10-py3-none-any.whl", hash = "sha256:2b06ad9e8d50c7a8db80e3b56dab590137b323410605af2be20d62a5f1ba1dc8"},
{file = "python_multipart-0.0.10.tar.gz", hash = "sha256:46eb3c6ce6fdda5fb1a03c7e11d490e407c6930a2703fe7aef4da71c374688fa"},
]
[[package]]
name = "python-slugify"
version = "8.0.4"
description = "A Python slugify application that also handles Unicode"
optional = false
python-versions = ">=3.7"
files = [
{file = "python-slugify-8.0.4.tar.gz", hash = "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856"},
{file = "python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8"},
]
[package.dependencies]
text-unidecode = ">=1.3"
[package.extras]
dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
unidecode = ["Unidecode (>=1.1.1)"]
[[package]]
name = "python-socketio"
@ -2016,13 +2161,13 @@ ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"
[[package]]
name = "reflex-chakra"
version = "0.6.0a7"
version = "0.6.0"
description = "reflex using chakra components"
optional = false
python-versions = "<4.0,>=3.8"
files = [
{file = "reflex_chakra-0.6.0a7-py3-none-any.whl", hash = "sha256:d693aee7323af13ce491165ffb4fe392344939e45516991671d5601727f749f4"},
{file = "reflex_chakra-0.6.0a7.tar.gz", hash = "sha256:fe17282e439fdfdfd507e24857a615258ef9789f15049aa0e70239fbcb4d5fbf"},
{file = "reflex_chakra-0.6.0-py3-none-any.whl", hash = "sha256:eca1593fca67289e05591dd21fbcc8632c119d64a08bdc41fd995055a114cc91"},
{file = "reflex_chakra-0.6.0.tar.gz", hash = "sha256:db1c7b48f1ba547bf91e5af103fce6fc7191d7225b414ebfbada7d983e33dd87"},
]
[package.dependencies]
@ -2161,13 +2306,13 @@ jeepney = ">=0.6"
[[package]]
name = "selenium"
version = "4.24.0"
version = "4.25.0"
description = "Official Python bindings for Selenium WebDriver"
optional = false
python-versions = ">=3.8"
files = [
{file = "selenium-4.24.0-py3-none-any.whl", hash = "sha256:42c23f60753d5415b261b236cecbd69bd4eb5271e1563915f546b443cb6b71c6"},
{file = "selenium-4.24.0.tar.gz", hash = "sha256:88281e5b5b90fe231868905d5ea745b9ee5e30db280b33498cc73fb0fa06d571"},
{file = "selenium-4.25.0-py3-none-any.whl", hash = "sha256:3798d2d12b4a570bc5790163ba57fef10b2afee958bf1d80f2a3cf07c4141f33"},
{file = "selenium-4.25.0.tar.gz", hash = "sha256:95d08d3b82fb353f3c474895154516604c7f0e6a9a565ae6498ef36c9bac6921"},
]
[package.dependencies]
@ -2358,17 +2503,18 @@ SQLAlchemy = ">=2.0.14,<2.1.0"
[[package]]
name = "starlette"
version = "0.38.5"
version = "0.38.6"
description = "The little ASGI library that shines."
optional = false
python-versions = ">=3.8"
files = [
{file = "starlette-0.38.5-py3-none-any.whl", hash = "sha256:632f420a9d13e3ee2a6f18f437b0a9f1faecb0bc42e1942aa2ea0e379a4c4206"},
{file = "starlette-0.38.5.tar.gz", hash = "sha256:04a92830a9b6eb1442c766199d62260c3d4dc9c4f9188360626b1e0273cb7077"},
{file = "starlette-0.38.6-py3-none-any.whl", hash = "sha256:4517a1409e2e73ee4951214ba012052b9e16f60e90d73cfb06192c19203bbb05"},
{file = "starlette-0.38.6.tar.gz", hash = "sha256:863a1588f5574e70a821dadefb41e4881ea451a47a3cd1b4df359d4ffefe5ead"},
]
[package.dependencies]
anyio = ">=3.4.0,<5"
typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""}
[package.extras]
full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"]
@ -2425,6 +2571,17 @@ files = [
doc = ["reno", "sphinx"]
test = ["pytest", "tornado (>=4.5)", "typeguard"]
[[package]]
name = "text-unidecode"
version = "1.3"
description = "The most basic Text::Unidecode port"
optional = false
python-versions = "*"
files = [
{file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"},
{file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"},
]
[[package]]
name = "toml"
version = "0.10.2"
@ -2546,13 +2703,13 @@ files = [
[[package]]
name = "tzdata"
version = "2024.1"
version = "2024.2"
description = "Provider of IANA time zone data"
optional = false
python-versions = ">=2"
files = [
{file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"},
{file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"},
{file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
{file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
]
[[package]]
@ -2632,97 +2789,97 @@ test = ["websockets"]
[[package]]
name = "websockets"
version = "13.0.1"
version = "13.1"
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
optional = false
python-versions = ">=3.8"
files = [
{file = "websockets-13.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1841c9082a3ba4a05ea824cf6d99570a6a2d8849ef0db16e9c826acb28089e8f"},
{file = "websockets-13.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c5870b4a11b77e4caa3937142b650fbbc0914a3e07a0cf3131f35c0587489c1c"},
{file = "websockets-13.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f1d3d1f2eb79fe7b0fb02e599b2bf76a7619c79300fc55f0b5e2d382881d4f7f"},
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15c7d62ee071fa94a2fc52c2b472fed4af258d43f9030479d9c4a2de885fd543"},
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6724b554b70d6195ba19650fef5759ef11346f946c07dbbe390e039bcaa7cc3d"},
{file = "websockets-13.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a952fa2ae57a42ba7951e6b2605e08a24801a4931b5644dfc68939e041bc7f"},
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:17118647c0ea14796364299e942c330d72acc4b248e07e639d34b75067b3cdd8"},
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:64a11aae1de4c178fa653b07d90f2fb1a2ed31919a5ea2361a38760192e1858b"},
{file = "websockets-13.0.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:0617fd0b1d14309c7eab6ba5deae8a7179959861846cbc5cb528a7531c249448"},
{file = "websockets-13.0.1-cp310-cp310-win32.whl", hash = "sha256:11f9976ecbc530248cf162e359a92f37b7b282de88d1d194f2167b5e7ad80ce3"},
{file = "websockets-13.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:c3c493d0e5141ec055a7d6809a28ac2b88d5b878bb22df8c621ebe79a61123d0"},
{file = "websockets-13.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:699ba9dd6a926f82a277063603fc8d586b89f4cb128efc353b749b641fcddda7"},
{file = "websockets-13.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cf2fae6d85e5dc384bf846f8243ddaa9197f3a1a70044f59399af001fd1f51d4"},
{file = "websockets-13.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:52aed6ef21a0f1a2a5e310fb5c42d7555e9c5855476bbd7173c3aa3d8a0302f2"},
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8eb2b9a318542153674c6e377eb8cb9ca0fc011c04475110d3477862f15d29f0"},
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5df891c86fe68b2c38da55b7aea7095beca105933c697d719f3f45f4220a5e0e"},
{file = "websockets-13.0.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fac2d146ff30d9dd2fcf917e5d147db037a5c573f0446c564f16f1f94cf87462"},
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b8ac5b46fd798bbbf2ac6620e0437c36a202b08e1f827832c4bf050da081b501"},
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:46af561eba6f9b0848b2c9d2427086cabadf14e0abdd9fde9d72d447df268418"},
{file = "websockets-13.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b5a06d7f60bc2fc378a333978470dfc4e1415ee52f5f0fce4f7853eb10c1e9df"},
{file = "websockets-13.0.1-cp311-cp311-win32.whl", hash = "sha256:556e70e4f69be1082e6ef26dcb70efcd08d1850f5d6c5f4f2bcb4e397e68f01f"},
{file = "websockets-13.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:67494e95d6565bf395476e9d040037ff69c8b3fa356a886b21d8422ad86ae075"},
{file = "websockets-13.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f9c9e258e3d5efe199ec23903f5da0eeaad58cf6fccb3547b74fd4750e5ac47a"},
{file = "websockets-13.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6b41a1b3b561f1cba8321fb32987552a024a8f67f0d05f06fcf29f0090a1b956"},
{file = "websockets-13.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f73e676a46b0fe9426612ce8caeca54c9073191a77c3e9d5c94697aef99296af"},
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f613289f4a94142f914aafad6c6c87903de78eae1e140fa769a7385fb232fdf"},
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f52504023b1480d458adf496dc1c9e9811df4ba4752f0bc1f89ae92f4f07d0c"},
{file = "websockets-13.0.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:139add0f98206cb74109faf3611b7783ceafc928529c62b389917a037d4cfdf4"},
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:47236c13be337ef36546004ce8c5580f4b1150d9538b27bf8a5ad8edf23ccfab"},
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c44ca9ade59b2e376612df34e837013e2b273e6c92d7ed6636d0556b6f4db93d"},
{file = "websockets-13.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9bbc525f4be3e51b89b2a700f5746c2a6907d2e2ef4513a8daafc98198b92237"},
{file = "websockets-13.0.1-cp312-cp312-win32.whl", hash = "sha256:3624fd8664f2577cf8de996db3250662e259bfbc870dd8ebdcf5d7c6ac0b5185"},
{file = "websockets-13.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0513c727fb8adffa6d9bf4a4463b2bade0186cbd8c3604ae5540fae18a90cb99"},
{file = "websockets-13.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:1ee4cc030a4bdab482a37462dbf3ffb7e09334d01dd37d1063be1136a0d825fa"},
{file = "websockets-13.0.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dbb0b697cc0655719522406c059eae233abaa3243821cfdfab1215d02ac10231"},
{file = "websockets-13.0.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:acbebec8cb3d4df6e2488fbf34702cbc37fc39ac7abf9449392cefb3305562e9"},
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63848cdb6fcc0bf09d4a155464c46c64ffdb5807ede4fb251da2c2692559ce75"},
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:872afa52a9f4c414d6955c365b6588bc4401272c629ff8321a55f44e3f62b553"},
{file = "websockets-13.0.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05e70fec7c54aad4d71eae8e8cab50525e899791fc389ec6f77b95312e4e9920"},
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e82db3756ccb66266504f5a3de05ac6b32f287faacff72462612120074103329"},
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4e85f46ce287f5c52438bb3703d86162263afccf034a5ef13dbe4318e98d86e7"},
{file = "websockets-13.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3fea72e4e6edb983908f0db373ae0732b275628901d909c382aae3b592589f2"},
{file = "websockets-13.0.1-cp313-cp313-win32.whl", hash = "sha256:254ecf35572fca01a9f789a1d0f543898e222f7b69ecd7d5381d8d8047627bdb"},
{file = "websockets-13.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:ca48914cdd9f2ccd94deab5bcb5ac98025a5ddce98881e5cce762854a5de330b"},
{file = "websockets-13.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b74593e9acf18ea5469c3edaa6b27fa7ecf97b30e9dabd5a94c4c940637ab96e"},
{file = "websockets-13.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:132511bfd42e77d152c919147078460c88a795af16b50e42a0bd14f0ad71ddd2"},
{file = "websockets-13.0.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:165bedf13556f985a2aa064309baa01462aa79bf6112fbd068ae38993a0e1f1b"},
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e801ca2f448850685417d723ec70298feff3ce4ff687c6f20922c7474b4746ae"},
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30d3a1f041360f029765d8704eae606781e673e8918e6b2c792e0775de51352f"},
{file = "websockets-13.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67648f5e50231b5a7f6d83b32f9c525e319f0ddc841be0de64f24928cd75a603"},
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4f0426d51c8f0926a4879390f53c7f5a855e42d68df95fff6032c82c888b5f36"},
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:ef48e4137e8799998a343706531e656fdec6797b80efd029117edacb74b0a10a"},
{file = "websockets-13.0.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:249aab278810bee585cd0d4de2f08cfd67eed4fc75bde623be163798ed4db2eb"},
{file = "websockets-13.0.1-cp38-cp38-win32.whl", hash = "sha256:06c0a667e466fcb56a0886d924b5f29a7f0886199102f0a0e1c60a02a3751cb4"},
{file = "websockets-13.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1f3cf6d6ec1142412d4535adabc6bd72a63f5f148c43fe559f06298bc21953c9"},
{file = "websockets-13.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1fa082ea38d5de51dd409434edc27c0dcbd5fed2b09b9be982deb6f0508d25bc"},
{file = "websockets-13.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4a365bcb7be554e6e1f9f3ed64016e67e2fa03d7b027a33e436aecf194febb63"},
{file = "websockets-13.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10a0dc7242215d794fb1918f69c6bb235f1f627aaf19e77f05336d147fce7c37"},
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59197afd478545b1f73367620407b0083303569c5f2d043afe5363676f2697c9"},
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d20516990d8ad557b5abeb48127b8b779b0b7e6771a265fa3e91767596d7d97"},
{file = "websockets-13.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1a2e272d067030048e1fe41aa1ec8cfbbaabce733b3d634304fa2b19e5c897f"},
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ad327ac80ba7ee61da85383ca8822ff808ab5ada0e4a030d66703cc025b021c4"},
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:518f90e6dd089d34eaade01101fd8a990921c3ba18ebbe9b0165b46ebff947f0"},
{file = "websockets-13.0.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:68264802399aed6fe9652e89761031acc734fc4c653137a5911c2bfa995d6d6d"},
{file = "websockets-13.0.1-cp39-cp39-win32.whl", hash = "sha256:a5dc0c42ded1557cc7c3f0240b24129aefbad88af4f09346164349391dea8e58"},
{file = "websockets-13.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:b448a0690ef43db5ef31b3a0d9aea79043882b4632cfc3eaab20105edecf6097"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:faef9ec6354fe4f9a2c0bbb52fb1ff852effc897e2a4501e25eb3a47cb0a4f89"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:03d3f9ba172e0a53e37fa4e636b86cc60c3ab2cfee4935e66ed1d7acaa4625ad"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d450f5a7a35662a9b91a64aefa852f0c0308ee256122f5218a42f1d13577d71e"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f55b36d17ac50aa8a171b771e15fbe1561217510c8768af3d546f56c7576cdc"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14b9c006cac63772b31abbcd3e3abb6228233eec966bf062e89e7fa7ae0b7333"},
{file = "websockets-13.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b79915a1179a91f6c5f04ece1e592e2e8a6bd245a0e45d12fd56b2b59e559a32"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f40de079779acbcdbb6ed4c65af9f018f8b77c5ec4e17a4b737c05c2db554491"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80e4ba642fc87fa532bac07e5ed7e19d56940b6af6a8c61d4429be48718a380f"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a02b0161c43cc9e0232711eff846569fad6ec836a7acab16b3cf97b2344c060"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6aa74a45d4cdc028561a7d6ab3272c8b3018e23723100b12e58be9dfa5a24491"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00fd961943b6c10ee6f0b1130753e50ac5dcd906130dcd77b0003c3ab797d026"},
{file = "websockets-13.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d93572720d781331fb10d3da9ca1067817d84ad1e7c31466e9f5e59965618096"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:71e6e5a3a3728886caee9ab8752e8113670936a193284be9d6ad2176a137f376"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:c4a6343e3b0714e80da0b0893543bf9a5b5fa71b846ae640e56e9abc6fbc4c83"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a678532018e435396e37422a95e3ab87f75028ac79570ad11f5bf23cd2a7d8c"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6716c087e4aa0b9260c4e579bb82e068f84faddb9bfba9906cb87726fa2e870"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e33505534f3f673270dd67f81e73550b11de5b538c56fe04435d63c02c3f26b5"},
{file = "websockets-13.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:acab3539a027a85d568c2573291e864333ec9d912675107d6efceb7e2be5d980"},
{file = "websockets-13.0.1-py3-none-any.whl", hash = "sha256:b80f0c51681c517604152eb6a572f5a9378f877763231fddb883ba2f968e8817"},
{file = "websockets-13.0.1.tar.gz", hash = "sha256:4d6ece65099411cfd9a48d13701d7438d9c34f479046b34c50ff60bb8834e43e"},
{file = "websockets-13.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f48c749857f8fb598fb890a75f540e3221d0976ed0bf879cf3c7eef34151acee"},
{file = "websockets-13.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c7e72ce6bda6fb9409cc1e8164dd41d7c91466fb599eb047cfda72fe758a34a7"},
{file = "websockets-13.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f779498eeec470295a2b1a5d97aa1bc9814ecd25e1eb637bd9d1c73a327387f6"},
{file = "websockets-13.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4676df3fe46956fbb0437d8800cd5f2b6d41143b6e7e842e60554398432cf29b"},
{file = "websockets-13.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a7affedeb43a70351bb811dadf49493c9cfd1ed94c9c70095fd177e9cc1541fa"},
{file = "websockets-13.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1971e62d2caa443e57588e1d82d15f663b29ff9dfe7446d9964a4b6f12c1e700"},
{file = "websockets-13.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:5f2e75431f8dc4a47f31565a6e1355fb4f2ecaa99d6b89737527ea917066e26c"},
{file = "websockets-13.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:58cf7e75dbf7e566088b07e36ea2e3e2bd5676e22216e4cad108d4df4a7402a0"},
{file = "websockets-13.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c90d6dec6be2c7d03378a574de87af9b1efea77d0c52a8301dd831ece938452f"},
{file = "websockets-13.1-cp310-cp310-win32.whl", hash = "sha256:730f42125ccb14602f455155084f978bd9e8e57e89b569b4d7f0f0c17a448ffe"},
{file = "websockets-13.1-cp310-cp310-win_amd64.whl", hash = "sha256:5993260f483d05a9737073be197371940c01b257cc45ae3f1d5d7adb371b266a"},
{file = "websockets-13.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:61fc0dfcda609cda0fc9fe7977694c0c59cf9d749fbb17f4e9483929e3c48a19"},
{file = "websockets-13.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ceec59f59d092c5007e815def4ebb80c2de330e9588e101cf8bd94c143ec78a5"},
{file = "websockets-13.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c1dca61c6db1166c48b95198c0b7d9c990b30c756fc2923cc66f68d17dc558fd"},
{file = "websockets-13.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308e20f22c2c77f3f39caca508e765f8725020b84aa963474e18c59accbf4c02"},
{file = "websockets-13.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d516c325e6540e8a57b94abefc3459d7dab8ce52ac75c96cad5549e187e3a7"},
{file = "websockets-13.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87c6e35319b46b99e168eb98472d6c7d8634ee37750d7693656dc766395df096"},
{file = "websockets-13.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5f9fee94ebafbc3117c30be1844ed01a3b177bb6e39088bc6b2fa1dc15572084"},
{file = "websockets-13.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7c1e90228c2f5cdde263253fa5db63e6653f1c00e7ec64108065a0b9713fa1b3"},
{file = "websockets-13.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6548f29b0e401eea2b967b2fdc1c7c7b5ebb3eeb470ed23a54cd45ef078a0db9"},
{file = "websockets-13.1-cp311-cp311-win32.whl", hash = "sha256:c11d4d16e133f6df8916cc5b7e3e96ee4c44c936717d684a94f48f82edb7c92f"},
{file = "websockets-13.1-cp311-cp311-win_amd64.whl", hash = "sha256:d04f13a1d75cb2b8382bdc16ae6fa58c97337253826dfe136195b7f89f661557"},
{file = "websockets-13.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:9d75baf00138f80b48f1eac72ad1535aac0b6461265a0bcad391fc5aba875cfc"},
{file = "websockets-13.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9b6f347deb3dcfbfde1c20baa21c2ac0751afaa73e64e5b693bb2b848efeaa49"},
{file = "websockets-13.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de58647e3f9c42f13f90ac7e5f58900c80a39019848c5547bc691693098ae1bd"},
{file = "websockets-13.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1b54689e38d1279a51d11e3467dd2f3a50f5f2e879012ce8f2d6943f00e83f0"},
{file = "websockets-13.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf1781ef73c073e6b0f90af841aaf98501f975d306bbf6221683dd594ccc52b6"},
{file = "websockets-13.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d23b88b9388ed85c6faf0e74d8dec4f4d3baf3ecf20a65a47b836d56260d4b9"},
{file = "websockets-13.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3c78383585f47ccb0fcf186dcb8a43f5438bd7d8f47d69e0b56f71bf431a0a68"},
{file = "websockets-13.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:d6d300f8ec35c24025ceb9b9019ae9040c1ab2f01cddc2bcc0b518af31c75c14"},
{file = "websockets-13.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a9dcaf8b0cc72a392760bb8755922c03e17a5a54e08cca58e8b74f6902b433cf"},
{file = "websockets-13.1-cp312-cp312-win32.whl", hash = "sha256:2f85cf4f2a1ba8f602298a853cec8526c2ca42a9a4b947ec236eaedb8f2dc80c"},
{file = "websockets-13.1-cp312-cp312-win_amd64.whl", hash = "sha256:38377f8b0cdeee97c552d20cf1865695fcd56aba155ad1b4ca8779a5b6ef4ac3"},
{file = "websockets-13.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a9ab1e71d3d2e54a0aa646ab6d4eebfaa5f416fe78dfe4da2839525dc5d765c6"},
{file = "websockets-13.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b9d7439d7fab4dce00570bb906875734df13d9faa4b48e261c440a5fec6d9708"},
{file = "websockets-13.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:327b74e915cf13c5931334c61e1a41040e365d380f812513a255aa804b183418"},
{file = "websockets-13.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:325b1ccdbf5e5725fdcb1b0e9ad4d2545056479d0eee392c291c1bf76206435a"},
{file = "websockets-13.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:346bee67a65f189e0e33f520f253d5147ab76ae42493804319b5716e46dddf0f"},
{file = "websockets-13.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a0fa841646320ec0d3accdff5b757b06e2e5c86ba32af2e0815c96c7a603c5"},
{file = "websockets-13.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:18503d2c5f3943e93819238bf20df71982d193f73dcecd26c94514f417f6b135"},
{file = "websockets-13.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:a9cd1af7e18e5221d2878378fbc287a14cd527fdd5939ed56a18df8a31136bb2"},
{file = "websockets-13.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70c5be9f416aa72aab7a2a76c90ae0a4fe2755c1816c153c1a2bcc3333ce4ce6"},
{file = "websockets-13.1-cp313-cp313-win32.whl", hash = "sha256:624459daabeb310d3815b276c1adef475b3e6804abaf2d9d2c061c319f7f187d"},
{file = "websockets-13.1-cp313-cp313-win_amd64.whl", hash = "sha256:c518e84bb59c2baae725accd355c8dc517b4a3ed8db88b4bc93c78dae2974bf2"},
{file = "websockets-13.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c7934fd0e920e70468e676fe7f1b7261c1efa0d6c037c6722278ca0228ad9d0d"},
{file = "websockets-13.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:149e622dc48c10ccc3d2760e5f36753db9cacf3ad7bc7bbbfd7d9c819e286f23"},
{file = "websockets-13.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a569eb1b05d72f9bce2ebd28a1ce2054311b66677fcd46cf36204ad23acead8c"},
{file = "websockets-13.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95df24ca1e1bd93bbca51d94dd049a984609687cb2fb08a7f2c56ac84e9816ea"},
{file = "websockets-13.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8dbb1bf0c0a4ae8b40bdc9be7f644e2f3fb4e8a9aca7145bfa510d4a374eeb7"},
{file = "websockets-13.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:035233b7531fb92a76beefcbf479504db8c72eb3bff41da55aecce3a0f729e54"},
{file = "websockets-13.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:e4450fc83a3df53dec45922b576e91e94f5578d06436871dce3a6be38e40f5db"},
{file = "websockets-13.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:463e1c6ec853202dd3657f156123d6b4dad0c546ea2e2e38be2b3f7c5b8e7295"},
{file = "websockets-13.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:6d6855bbe70119872c05107e38fbc7f96b1d8cb047d95c2c50869a46c65a8e96"},
{file = "websockets-13.1-cp38-cp38-win32.whl", hash = "sha256:204e5107f43095012b00f1451374693267adbb832d29966a01ecc4ce1db26faf"},
{file = "websockets-13.1-cp38-cp38-win_amd64.whl", hash = "sha256:485307243237328c022bc908b90e4457d0daa8b5cf4b3723fd3c4a8012fce4c6"},
{file = "websockets-13.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9b37c184f8b976f0c0a231a5f3d6efe10807d41ccbe4488df8c74174805eea7d"},
{file = "websockets-13.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:163e7277e1a0bd9fb3c8842a71661ad19c6aa7bb3d6678dc7f89b17fbcc4aeb7"},
{file = "websockets-13.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b889dbd1342820cc210ba44307cf75ae5f2f96226c0038094455a96e64fb07a"},
{file = "websockets-13.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:586a356928692c1fed0eca68b4d1c2cbbd1ca2acf2ac7e7ebd3b9052582deefa"},
{file = "websockets-13.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7bd6abf1e070a6b72bfeb71049d6ad286852e285f146682bf30d0296f5fbadfa"},
{file = "websockets-13.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2aad13a200e5934f5a6767492fb07151e1de1d6079c003ab31e1823733ae79"},
{file = "websockets-13.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:df01aea34b6e9e33572c35cd16bae5a47785e7d5c8cb2b54b2acdb9678315a17"},
{file = "websockets-13.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:e54affdeb21026329fb0744ad187cf812f7d3c2aa702a5edb562b325191fcab6"},
{file = "websockets-13.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:9ef8aa8bdbac47f4968a5d66462a2a0935d044bf35c0e5a8af152d58516dbeb5"},
{file = "websockets-13.1-cp39-cp39-win32.whl", hash = "sha256:deeb929efe52bed518f6eb2ddc00cc496366a14c726005726ad62c2dd9017a3c"},
{file = "websockets-13.1-cp39-cp39-win_amd64.whl", hash = "sha256:7c65ffa900e7cc958cd088b9a9157a8141c991f8c53d11087e6fb7277a03f81d"},
{file = "websockets-13.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5dd6da9bec02735931fccec99d97c29f47cc61f644264eb995ad6c0c27667238"},
{file = "websockets-13.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:2510c09d8e8df777177ee3d40cd35450dc169a81e747455cc4197e63f7e7bfe5"},
{file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1c3cf67185543730888b20682fb186fc8d0fa6f07ccc3ef4390831ab4b388d9"},
{file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcc03c8b72267e97b49149e4863d57c2d77f13fae12066622dc78fe322490fe6"},
{file = "websockets-13.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:004280a140f220c812e65f36944a9ca92d766b6cc4560be652a0a3883a79ed8a"},
{file = "websockets-13.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:e2620453c075abeb0daa949a292e19f56de518988e079c36478bacf9546ced23"},
{file = "websockets-13.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9156c45750b37337f7b0b00e6248991a047be4aa44554c9886fe6bdd605aab3b"},
{file = "websockets-13.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:80c421e07973a89fbdd93e6f2003c17d20b69010458d3a8e37fb47874bd67d51"},
{file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:82d0ba76371769d6a4e56f7e83bb8e81846d17a6190971e38b5de108bde9b0d7"},
{file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9875a0143f07d74dc5e1ded1c4581f0d9f7ab86c78994e2ed9e95050073c94d"},
{file = "websockets-13.1-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a11e38ad8922c7961447f35c7b17bffa15de4d17c70abd07bfbe12d6faa3e027"},
{file = "websockets-13.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4059f790b6ae8768471cddb65d3c4fe4792b0ab48e154c9f0a04cefaabcd5978"},
{file = "websockets-13.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:25c35bf84bf7c7369d247f0b8cfa157f989862c49104c5cf85cb5436a641d93e"},
{file = "websockets-13.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:83f91d8a9bb404b8c2c41a707ac7f7f75b9442a0a876df295de27251a856ad09"},
{file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a43cfdcddd07f4ca2b1afb459824dd3c6d53a51410636a2c7fc97b9a8cf4842"},
{file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48a2ef1381632a2f0cb4efeff34efa97901c9fbc118e01951ad7cfc10601a9bb"},
{file = "websockets-13.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:459bf774c754c35dbb487360b12c5727adab887f1622b8aed5755880a21c4a20"},
{file = "websockets-13.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:95858ca14a9f6fa8413d29e0a585b31b278388aa775b8a81fa24830123874678"},
{file = "websockets-13.1-py3-none-any.whl", hash = "sha256:a9a396a6ad26130cdae92ae10c36af09d9bfe6cafe69670fd3b6da9b07b4044f"},
{file = "websockets-13.1.tar.gz", hash = "sha256:a3b3366087c1bc0a2795111edcadddb8b3b59509d5db5d7ea3fdd69f954a8878"},
]
[[package]]
@ -2853,5 +3010,5 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.0"
python-versions = "^3.10"
content-hash = "1c8904971be7061c2b33400d2b5f461e526703586e5b7f9eb08c2fbfda1a23ab"
python-versions = "^3.9"
content-hash = "adccd071775567aeefe219261aeb9e222906c865745f03edb1e770edc79c44ac"

View File

@ -1,6 +1,6 @@
[tool.poetry]
name = "reflex"
version = "0.6.0a1"
version = "0.6.2dev1"
description = "Web apps in pure Python."
license = "Apache-2.0"
authors = [
@ -26,7 +26,7 @@ packages = [
]
[tool.poetry.dependencies]
python = "^3.10"
python = "^3.9"
dill = ">=0.3.8,<0.4"
fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
gunicorn = ">=20.1.0,<24.0"
@ -59,7 +59,7 @@ httpx = ">=0.25.1,<1.0"
twine = ">=4.0.0,<6.0"
tomlkit = ">=0.12.4,<1.0"
lazy_loader = ">=0.4"
reflex-chakra = ">=0.6.0a6"
reflex-chakra = ">=0.6.0"
[tool.poetry.group.dev.dependencies]
pytest = ">=7.1.2,<8.0"
@ -77,6 +77,8 @@ asynctest = ">=0.13.0,<1.0"
pre-commit = ">=3.2.1"
selenium = ">=4.11.0,<5.0"
pytest-benchmark = ">=4.0.0,<5.0"
playwright = ">=1.46.0"
pytest-playwright = ">=0.5.1"
[tool.poetry.scripts]
reflex = "reflex.reflex:cli"
@ -88,7 +90,7 @@ build-backend = "poetry.core.masonry.api"
[tool.pyright]
[tool.ruff]
target-version = "py310"
target-version = "py39"
lint.select = ["B", "D", "E", "F", "I", "SIM", "W"]
lint.ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"]
lint.pydocstyle.convention = "google"

View File

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

View File

@ -832,7 +832,9 @@ export const useEventLoop = (
* @returns True if the value is truthy, false otherwise.
*/
export const isTrue = (val) => {
return Array.isArray(val) ? val.length > 0 : !!val;
if (Array.isArray(val)) return val.length > 0;
if (val === Object(val)) return Object.keys(val).length > 0;
return Boolean(val);
};
/**

View File

@ -509,9 +509,8 @@ class App(MiddlewareMixin, LifespanMixin, Base):
"""
# If the route is not set, get it from the callable.
if route is None:
assert isinstance(
component, Callable
), "Route must be set if component is not a callable."
if not isinstance(component, Callable):
raise ValueError("Route must be set if component is not a callable.")
# Format the route.
route = format.format_route(component.__name__)
else:
@ -615,10 +614,7 @@ class App(MiddlewareMixin, LifespanMixin, Base):
for route in self.pages:
replaced_route = replace_brackets_with_keywords(route)
for rw, r, nr in zip(
replaced_route.split("/"),
route.split("/"),
new_route.split("/"),
strict=False,
replaced_route.split("/"), route.split("/"), new_route.split("/")
):
if rw in segments and r != nr:
# If the slugs in the segments of both routes are not the same, then the route is invalid
@ -1203,7 +1199,6 @@ class App(MiddlewareMixin, LifespanMixin, Base):
FRONTEND_ARG_SPEC,
BACKEND_ARG_SPEC,
],
strict=False,
):
if hasattr(handler_fn, "__name__"):
_fn_name = handler_fn.__name__
@ -1563,6 +1558,9 @@ class EventNamespace(AsyncNamespace):
async def on_event(self, sid, data):
"""Event for receiving front-end websocket events.
Raises:
RuntimeError: If the Socket.IO is badly initialized.
Args:
sid: The Socket.IO session id.
data: The event data.
@ -1575,9 +1573,11 @@ class EventNamespace(AsyncNamespace):
self.sid_to_token[sid] = event.token
# Get the event environment.
assert self.app.sio is not None
if self.app.sio is None:
raise RuntimeError("Socket.IO is not initialized.")
environ = self.app.sio.get_environ(sid, self.namespace)
assert environ is not None
if environ is None:
raise RuntimeError("Socket.IO environ is not initialized.")
# Get the client headers.
headers = {

View File

@ -6,11 +6,13 @@ import asyncio
import contextlib
import functools
import inspect
import sys
from typing import Callable, Coroutine, Set, Union
from fastapi import FastAPI
from reflex.utils import console
from reflex.utils.exceptions import InvalidLifespanTaskType
from .mixin import AppMixin
@ -26,6 +28,7 @@ class LifespanMixin(AppMixin):
try:
async with contextlib.AsyncExitStack() as stack:
for task in self.lifespan_tasks:
run_msg = f"Started lifespan task: {task.__name__} as {{type}}" # type: ignore
if isinstance(task, asyncio.Task):
running_tasks.append(task)
else:
@ -35,15 +38,19 @@ class LifespanMixin(AppMixin):
_t = task()
if isinstance(_t, contextlib._AsyncGeneratorContextManager):
await stack.enter_async_context(_t)
console.debug(run_msg.format(type="asynccontextmanager"))
elif isinstance(_t, Coroutine):
running_tasks.append(asyncio.create_task(_t))
task_ = asyncio.create_task(_t)
task_.add_done_callback(lambda t: t.result())
running_tasks.append(task_)
console.debug(run_msg.format(type="coroutine"))
else:
console.debug(run_msg.format(type="function"))
yield
finally:
cancel_kwargs = (
{"msg": "lifespan_cleanup"} if sys.version_info >= (3, 9) else {}
)
for task in running_tasks:
task.cancel(**cancel_kwargs)
console.debug(f"Canceling lifespan task: {task}")
task.cancel(msg="lifespan_cleanup")
def register_lifespan_task(self, task: Callable | asyncio.Task, **task_kwargs):
"""Register a task to run during the lifespan of the app.
@ -51,7 +58,18 @@ class LifespanMixin(AppMixin):
Args:
task: The task to register.
task_kwargs: The kwargs of the task.
Raises:
InvalidLifespanTaskType: If the task is a generator function.
"""
if inspect.isgeneratorfunction(task) or inspect.isasyncgenfunction(task):
raise InvalidLifespanTaskType(
f"Task {task.__name__} of type generator must be decorated with contextlib.asynccontextmanager."
)
if task_kwargs:
original_task = task
task = functools.partial(task, **task_kwargs) # type: ignore
functools.update_wrapper(task, original_task) # type: ignore
self.lifespan_tasks.add(task) # type: ignore
console.debug(f"Registered lifespan task: {task.__name__}") # type: ignore

View File

@ -15,7 +15,7 @@ if constants.CompileVars.APP != "app":
telemetry.send("compile")
app_module = get_app(reload=False)
app = getattr(app_module, constants.CompileVars.APP)
# For py3.8 and 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).
app._apply_decorated_pages()
compile_future = ThreadPoolExecutor(max_workers=1).submit(app._compile)

View File

@ -44,6 +44,9 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
Args:
fields: The set of fields to import from the library.
Raises:
ValueError: If there is more than one default import.
Returns:
The libraries for default and rest.
default: default library. When install "import def from library".
@ -54,7 +57,8 @@ def compile_import_statement(fields: list[ImportVar]) -> tuple[str, list[str]]:
# Check for default imports.
defaults = {field for field in fields_set if field.is_default}
assert len(defaults) < 2
if len(defaults) >= 2:
raise ValueError("Only one default import is allowed.")
# Get the default import, and the specific imports.
default = next(iter({field.name for field in defaults}), "")
@ -92,6 +96,9 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
Args:
import_dict: The import dict to compile.
Raises:
ValueError: If an import in the dict is invalid.
Returns:
The list of import dict.
"""
@ -106,8 +113,10 @@ def compile_imports(import_dict: ParsedImportDict) -> list[dict]:
continue
if not lib:
assert not default, "No default field allowed for empty library."
assert rest is not None and len(rest) > 0, "No fields to import."
if default:
raise ValueError("No default field allowed for empty library.")
if rest is None or len(rest) == 0:
raise ValueError("No fields to import.")
for module in sorted(rest):
import_dicts.append(get_import_dict(module))
continue
@ -155,7 +164,7 @@ def compile_state(state: Type[BaseState]) -> dict:
initial_state = state(_reflex_internal_init=True).dict(
initial=True, include_computed=False
)
return format.format_state(initial_state)
return initial_state
def _compile_client_storage_field(
@ -429,11 +438,11 @@ def add_meta(
Returns:
The component with the metadata added.
"""
meta_tags = [Meta.create(**item) for item in meta]
children: list[Any] = [
Title.create(title),
meta_tags = [
item if isinstance(item, Component) else Meta.create(**item) for item in meta
]
children: list[Any] = [Title.create(title)]
if description:
children.append(Description.create(content=description))
children.append(Image.create(content=image))

View File

@ -7,7 +7,7 @@ from typing import Any, Iterator
from reflex.components.component import Component
from reflex.components.tags import Tag
from reflex.components.tags.tagless import Tagless
from reflex.vars.base import Var
from reflex.vars import ArrayVar, BooleanVar, ObjectVar, Var
class Bare(Component):
@ -33,6 +33,8 @@ class Bare(Component):
def _render(self) -> Tag:
if isinstance(self.contents, Var):
if isinstance(self.contents, (BooleanVar, ObjectVar, ArrayVar)):
return Tagless(contents=f"{{{str(self.contents.to_string())}}}")
return Tagless(contents=f"{{{str(self.contents)}}}")
return Tagless(contents=str(self.contents))

View File

@ -16,13 +16,15 @@ class Title(Component):
def render(self) -> dict:
"""Render the title component.
Raises:
ValueError: If the title is not a single string.
Returns:
The rendered title component.
"""
# Make sure the title is a single string.
assert len(self.children) == 1 and isinstance(
self.children[0], Bare
), "Title must be a single string."
if len(self.children) != 1 or not isinstance(self.children[0], Bare):
raise ValueError("Title must be a single string.")
return super().render()

View File

@ -55,6 +55,7 @@ from reflex.utils.imports import (
)
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from reflex.vars.sequence import LiteralArrayVar
class BaseComponent(Base, ABC):
@ -448,8 +449,16 @@ class Component(BaseComponent, ABC):
and not types._issubclass(passed_type, expected_type, value)
):
value_name = value._js_expr if isinstance(value, Var) else value
additional_info = (
" You can call `.bool()` on the value to convert it to a boolean."
if expected_type is bool and isinstance(value, Var)
else ""
)
raise TypeError(
f"Invalid var passed for prop {type(self).__name__}.{key}, expected type {expected_type}, got value {value_name} of type {passed_type}."
+ additional_info
)
# Check if the key is an event trigger.
if key in component_specific_triggers:
@ -469,7 +478,7 @@ class Component(BaseComponent, ABC):
# Merge styles, the later ones overriding keys in the earlier ones.
style = {k: v for style_dict in style for k, v in style_dict.items()}
if isinstance(style, Breakpoints):
if isinstance(style, (Breakpoints, Var)):
style = {
# Assign the Breakpoints to the self-referential selector to avoid squashing down to a regular dict.
"&": style,
@ -488,6 +497,11 @@ class Component(BaseComponent, ABC):
# Convert class_name to str if it's list
class_name = kwargs.get("class_name", "")
if isinstance(class_name, (List, tuple)):
if any(isinstance(c, Var) for c in class_name):
kwargs["class_name"] = LiteralArrayVar.create(
class_name, _var_type=List[str]
).join(" ")
else:
kwargs["class_name"] = " ".join(class_name)
# Construct the component.
@ -1730,10 +1744,14 @@ class CustomComponent(Component):
Args:
seen: The tags of the components that have already been seen.
Raises:
ValueError: If the tag is not set.
Returns:
The set of custom components.
"""
assert self.tag is not None, "The tag must be set."
if self.tag is None:
raise ValueError("The tag must be set.")
# Store the seen components in a set to avoid infinite recursion.
if seen is None:

View File

@ -82,9 +82,7 @@ class Breakpoints(Dict[K, V]):
return Breakpoints(
{
k: v
for k, v in zip(
["initial", *breakpoint_names], thresholds, strict=False
)
for k, v in zip(["initial", *breakpoint_names], thresholds)
if v is not None
}
)

View File

@ -138,13 +138,13 @@ def cond(condition: Any, c1: Any, c2: Any = None) -> Component | Var:
"""
# Convert the condition to a Var.
cond_var = LiteralVar.create(condition)
assert cond_var is not None, "The condition must be set."
if cond_var is None:
raise ValueError("The condition must be set.")
# If the first component is a component, create a Cond component.
if isinstance(c1, BaseComponent):
assert c2 is None or isinstance(
c2, BaseComponent
), "Both arguments must be components."
if c2 is not None and not isinstance(c2, BaseComponent):
raise ValueError("Both arguments must be components.")
return Cond.create(cond_var, c1, c2)
# Otherwise, create a conditional Var.

View File

@ -8,7 +8,6 @@ _SUBMOD_ATTRS: dict[str, list[str]] = {
"code": [
"CodeBlock",
"code_block",
"LiteralCodeBlockTheme",
"LiteralCodeLanguage",
],
"dataeditor": ["data_editor", "data_editor_theme", "DataEditorTheme"],

View File

@ -4,7 +4,6 @@
# ------------------------------------------------------
from .code import CodeBlock as CodeBlock
from .code import LiteralCodeBlockTheme as LiteralCodeBlockTheme
from .code import LiteralCodeLanguage as LiteralCodeLanguage
from .code import code_block as code_block
from .dataeditor import DataEditorTheme as DataEditorTheme

View File

@ -2,10 +2,8 @@
from __future__ import annotations
import enum
from typing import Any, Dict, Literal, Optional, Union
from typing_extensions import get_args
import dataclasses
from typing import ClassVar, Dict, Literal, Optional, Union
from reflex.components.component import Component, ComponentNamespace
from reflex.components.core.cond import color_mode_cond
@ -19,55 +17,6 @@ from reflex.utils import console, format
from reflex.utils.imports import ImportDict, ImportVar
from reflex.vars.base import LiteralVar, Var, VarData
LiteralCodeBlockTheme = Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light", # not present in react-syntax-highlighter styles
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
]
LiteralCodeLanguage = Literal[
"abap",
"abnf",
@ -351,18 +300,82 @@ LiteralCodeLanguage = Literal[
]
def replace_quotes_with_camel_case(value: str) -> str:
"""Replaces quotes in the given string with camel case format.
def construct_theme_var(theme: str) -> Var[Theme]:
"""Construct a theme var.
Args:
value (str): The string to be processed.
theme: The theme to construct.
Returns:
str: The processed string with quotes replaced by camel case.
The constructed theme var.
"""
for theme in get_args(LiteralCodeBlockTheme):
value = value.replace(f'"{theme}"', format.to_camel_case(theme))
return value
return Var(
theme,
_var_data=VarData(
imports={
f"react-syntax-highlighter/dist/cjs/styles/prism/{format.to_kebab_case(theme)}": [
ImportVar(tag=theme, is_default=True, install=False)
]
}
),
)
@dataclasses.dataclass(init=False)
class Theme:
"""Themes for the CodeBlock component."""
a11y_dark: ClassVar[Var[Theme]] = construct_theme_var("a11yDark")
atom_dark: ClassVar[Var[Theme]] = construct_theme_var("atomDark")
cb: ClassVar[Var[Theme]] = construct_theme_var("cb")
coldark_cold: ClassVar[Var[Theme]] = construct_theme_var("coldarkCold")
coldark_dark: ClassVar[Var[Theme]] = construct_theme_var("coldarkDark")
coy: ClassVar[Var[Theme]] = construct_theme_var("coy")
coy_without_shadows: ClassVar[Var[Theme]] = construct_theme_var("coyWithoutShadows")
darcula: ClassVar[Var[Theme]] = construct_theme_var("darcula")
dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
dracula: ClassVar[Var[Theme]] = construct_theme_var("dracula")
duotone_dark: ClassVar[Var[Theme]] = construct_theme_var("duotoneDark")
duotone_earth: ClassVar[Var[Theme]] = construct_theme_var("duotoneEarth")
duotone_forest: ClassVar[Var[Theme]] = construct_theme_var("duotoneForest")
duotone_light: ClassVar[Var[Theme]] = construct_theme_var("duotoneLight")
duotone_sea: ClassVar[Var[Theme]] = construct_theme_var("duotoneSea")
duotone_space: ClassVar[Var[Theme]] = construct_theme_var("duotoneSpace")
funky: ClassVar[Var[Theme]] = construct_theme_var("funky")
ghcolors: ClassVar[Var[Theme]] = construct_theme_var("ghcolors")
gruvbox_dark: ClassVar[Var[Theme]] = construct_theme_var("gruvboxDark")
gruvbox_light: ClassVar[Var[Theme]] = construct_theme_var("gruvboxLight")
holi_theme: ClassVar[Var[Theme]] = construct_theme_var("holiTheme")
hopscotch: ClassVar[Var[Theme]] = construct_theme_var("hopscotch")
light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
lucario: ClassVar[Var[Theme]] = construct_theme_var("lucario")
material_dark: ClassVar[Var[Theme]] = construct_theme_var("materialDark")
material_light: ClassVar[Var[Theme]] = construct_theme_var("materialLight")
material_oceanic: ClassVar[Var[Theme]] = construct_theme_var("materialOceanic")
night_owl: ClassVar[Var[Theme]] = construct_theme_var("nightOwl")
nord: ClassVar[Var[Theme]] = construct_theme_var("nord")
okaidia: ClassVar[Var[Theme]] = construct_theme_var("okaidia")
one_dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
one_light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
pojoaque: ClassVar[Var[Theme]] = construct_theme_var("pojoaque")
prism: ClassVar[Var[Theme]] = construct_theme_var("prism")
shades_of_purple: ClassVar[Var[Theme]] = construct_theme_var("shadesOfPurple")
solarized_dark_atom: ClassVar[Var[Theme]] = construct_theme_var("solarizedDarkAtom")
solarizedlight: ClassVar[Var[Theme]] = construct_theme_var("solarizedlight")
synthwave84: ClassVar[Var[Theme]] = construct_theme_var("synthwave84")
tomorrow: ClassVar[Var[Theme]] = construct_theme_var("tomorrow")
twilight: ClassVar[Var[Theme]] = construct_theme_var("twilight")
vs: ClassVar[Var[Theme]] = construct_theme_var("vs")
vs_dark: ClassVar[Var[Theme]] = construct_theme_var("vsDark")
vsc_dark_plus: ClassVar[Var[Theme]] = construct_theme_var("vscDarkPlus")
xonokai: ClassVar[Var[Theme]] = construct_theme_var("xonokai")
z_touch: ClassVar[Var[Theme]] = construct_theme_var("zTouch")
for theme_name in dir(Theme):
if theme_name.startswith("_"):
continue
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
class CodeBlock(Component):
@ -375,7 +388,7 @@ class CodeBlock(Component):
alias = "SyntaxHighlighter"
# The theme to use ("light" or "dark").
theme: Var[Any] = "one-light" # type: ignore
theme: Var[Union[Theme, str]] = Theme.one_light
# The language to use.
language: Var[LiteralCodeLanguage] = "python" # type: ignore
@ -523,91 +536,6 @@ class CodeBlock(Component):
return out
@staticmethod
def convert_theme_name(theme) -> str:
"""Convert theme names to appropriate names.
Args:
theme: The theme name.
Returns:
The right theme name.
"""
if theme in ["light", "dark"]:
return f"one-{theme}"
return theme
def construct_theme_var(theme: str) -> Var:
"""Construct a theme var.
Args:
theme: The theme to construct.
Returns:
The constructed theme var.
"""
return Var(
theme,
_var_data=VarData(
imports={
f"react-syntax-highlighter/dist/cjs/styles/prism/{format.to_kebab_case(theme)}": [
ImportVar(tag=theme, is_default=True, install=False)
]
}
),
)
class Theme(enum.Enum):
"""Themes for the CodeBlock component."""
a11y_dark = construct_theme_var("a11yDark")
atom_dark = construct_theme_var("atomDark")
cb = construct_theme_var("cb")
coldark_cold = construct_theme_var("coldarkCold")
coldark_dark = construct_theme_var("coldarkDark")
coy = construct_theme_var("coy")
coy_without_shadows = construct_theme_var("coyWithoutShadows")
darcula = construct_theme_var("darcula")
dark = construct_theme_var("oneDark")
dracula = construct_theme_var("dracula")
duotone_dark = construct_theme_var("duotoneDark")
duotone_earth = construct_theme_var("duotoneEarth")
duotone_forest = construct_theme_var("duotoneForest")
duotone_light = construct_theme_var("duotoneLight")
duotone_sea = construct_theme_var("duotoneSea")
duotone_space = construct_theme_var("duotoneSpace")
funky = construct_theme_var("funky")
ghcolors = construct_theme_var("ghcolors")
gruvbox_dark = construct_theme_var("gruvboxDark")
gruvbox_light = construct_theme_var("gruvboxLight")
holi_theme = construct_theme_var("holiTheme")
hopscotch = construct_theme_var("hopscotch")
light = construct_theme_var("oneLight")
lucario = construct_theme_var("lucario")
material_dark = construct_theme_var("materialDark")
material_light = construct_theme_var("materialLight")
material_oceanic = construct_theme_var("materialOceanic")
night_owl = construct_theme_var("nightOwl")
nord = construct_theme_var("nord")
okaidia = construct_theme_var("okaidia")
one_dark = construct_theme_var("oneDark")
one_light = construct_theme_var("oneLight")
pojoaque = construct_theme_var("pojoaque")
prism = construct_theme_var("prism")
shades_of_purple = construct_theme_var("shadesOfPurple")
solarized_dark_atom = construct_theme_var("solarizedDarkAtom")
solarizedlight = construct_theme_var("solarizedlight")
synthwave84 = construct_theme_var("synthwave84")
tomorrow = construct_theme_var("tomorrow")
twilight = construct_theme_var("twilight")
vs = construct_theme_var("vs")
vs_dark = construct_theme_var("vsDark")
vsc_dark_plus = construct_theme_var("vscDarkPlus")
xonokai = construct_theme_var("xonokai")
z_touch = construct_theme_var("zTouch")
class CodeblockNamespace(ComponentNamespace):
"""Namespace for the CodeBlock component."""

View File

@ -3,8 +3,8 @@
# ------------------- DO NOT EDIT ----------------------
# This file was generated by `reflex/utils/pyi_generator.py`!
# ------------------------------------------------------
import enum
from typing import Any, Callable, Dict, Literal, Optional, Union, overload
import dataclasses
from typing import Any, Callable, ClassVar, Dict, Literal, Optional, Union, overload
from reflex.components.component import Component, ComponentNamespace
from reflex.constants.colors import Color
@ -13,53 +13,6 @@ from reflex.style import Style
from reflex.utils.imports import ImportDict
from reflex.vars.base import Var
LiteralCodeBlockTheme = Literal[
"a11y-dark",
"atom-dark",
"cb",
"coldark-cold",
"coldark-dark",
"coy",
"coy-without-shadows",
"darcula",
"dark",
"dracula",
"duotone-dark",
"duotone-earth",
"duotone-forest",
"duotone-light",
"duotone-sea",
"duotone-space",
"funky",
"ghcolors",
"gruvbox-dark",
"gruvbox-light",
"holi-theme",
"hopscotch",
"light",
"lucario",
"material-dark",
"material-light",
"material-oceanic",
"night-owl",
"nord",
"okaidia",
"one-dark",
"one-light",
"pojoaque",
"prism",
"shades-of-purple",
"solarized-dark-atom",
"solarizedlight",
"synthwave84",
"tomorrow",
"twilight",
"vs",
"vs-dark",
"vsc-dark-plus",
"xonokai",
"z-touch",
]
LiteralCodeLanguage = Literal[
"abap",
"abnf",
@ -342,7 +295,59 @@ LiteralCodeLanguage = Literal[
"zig",
]
def replace_quotes_with_camel_case(value: str) -> str: ...
def construct_theme_var(theme: str) -> Var[Theme]: ...
@dataclasses.dataclass(init=False)
class Theme:
a11y_dark: ClassVar[Var[Theme]] = construct_theme_var("a11yDark")
atom_dark: ClassVar[Var[Theme]] = construct_theme_var("atomDark")
cb: ClassVar[Var[Theme]] = construct_theme_var("cb")
coldark_cold: ClassVar[Var[Theme]] = construct_theme_var("coldarkCold")
coldark_dark: ClassVar[Var[Theme]] = construct_theme_var("coldarkDark")
coy: ClassVar[Var[Theme]] = construct_theme_var("coy")
coy_without_shadows: ClassVar[Var[Theme]] = construct_theme_var("coyWithoutShadows")
darcula: ClassVar[Var[Theme]] = construct_theme_var("darcula")
dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
dracula: ClassVar[Var[Theme]] = construct_theme_var("dracula")
duotone_dark: ClassVar[Var[Theme]] = construct_theme_var("duotoneDark")
duotone_earth: ClassVar[Var[Theme]] = construct_theme_var("duotoneEarth")
duotone_forest: ClassVar[Var[Theme]] = construct_theme_var("duotoneForest")
duotone_light: ClassVar[Var[Theme]] = construct_theme_var("duotoneLight")
duotone_sea: ClassVar[Var[Theme]] = construct_theme_var("duotoneSea")
duotone_space: ClassVar[Var[Theme]] = construct_theme_var("duotoneSpace")
funky: ClassVar[Var[Theme]] = construct_theme_var("funky")
ghcolors: ClassVar[Var[Theme]] = construct_theme_var("ghcolors")
gruvbox_dark: ClassVar[Var[Theme]] = construct_theme_var("gruvboxDark")
gruvbox_light: ClassVar[Var[Theme]] = construct_theme_var("gruvboxLight")
holi_theme: ClassVar[Var[Theme]] = construct_theme_var("holiTheme")
hopscotch: ClassVar[Var[Theme]] = construct_theme_var("hopscotch")
light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
lucario: ClassVar[Var[Theme]] = construct_theme_var("lucario")
material_dark: ClassVar[Var[Theme]] = construct_theme_var("materialDark")
material_light: ClassVar[Var[Theme]] = construct_theme_var("materialLight")
material_oceanic: ClassVar[Var[Theme]] = construct_theme_var("materialOceanic")
night_owl: ClassVar[Var[Theme]] = construct_theme_var("nightOwl")
nord: ClassVar[Var[Theme]] = construct_theme_var("nord")
okaidia: ClassVar[Var[Theme]] = construct_theme_var("okaidia")
one_dark: ClassVar[Var[Theme]] = construct_theme_var("oneDark")
one_light: ClassVar[Var[Theme]] = construct_theme_var("oneLight")
pojoaque: ClassVar[Var[Theme]] = construct_theme_var("pojoaque")
prism: ClassVar[Var[Theme]] = construct_theme_var("prism")
shades_of_purple: ClassVar[Var[Theme]] = construct_theme_var("shadesOfPurple")
solarized_dark_atom: ClassVar[Var[Theme]] = construct_theme_var("solarizedDarkAtom")
solarizedlight: ClassVar[Var[Theme]] = construct_theme_var("solarizedlight")
synthwave84: ClassVar[Var[Theme]] = construct_theme_var("synthwave84")
tomorrow: ClassVar[Var[Theme]] = construct_theme_var("tomorrow")
twilight: ClassVar[Var[Theme]] = construct_theme_var("twilight")
vs: ClassVar[Var[Theme]] = construct_theme_var("vs")
vs_dark: ClassVar[Var[Theme]] = construct_theme_var("vsDark")
vsc_dark_plus: ClassVar[Var[Theme]] = construct_theme_var("vscDarkPlus")
xonokai: ClassVar[Var[Theme]] = construct_theme_var("xonokai")
z_touch: ClassVar[Var[Theme]] = construct_theme_var("zTouch")
for theme_name in dir(Theme):
if theme_name.startswith("_"):
continue
setattr(Theme, theme_name, getattr(Theme, theme_name)._replace(_var_type=Theme))
class CodeBlock(Component):
def add_imports(self) -> ImportDict: ...
@ -353,7 +358,7 @@ class CodeBlock(Component):
*children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[Component, bool]] = None,
theme: Optional[Union[Any, Var[Any]]] = None,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[
Union[
Literal[
@ -999,57 +1004,6 @@ class CodeBlock(Component):
...
def add_style(self): ...
@staticmethod
def convert_theme_name(theme) -> str: ...
def construct_theme_var(theme: str) -> Var: ...
class Theme(enum.Enum):
a11y_dark = construct_theme_var("a11yDark")
atom_dark = construct_theme_var("atomDark")
cb = construct_theme_var("cb")
coldark_cold = construct_theme_var("coldarkCold")
coldark_dark = construct_theme_var("coldarkDark")
coy = construct_theme_var("coy")
coy_without_shadows = construct_theme_var("coyWithoutShadows")
darcula = construct_theme_var("darcula")
dark = construct_theme_var("oneDark")
dracula = construct_theme_var("dracula")
duotone_dark = construct_theme_var("duotoneDark")
duotone_earth = construct_theme_var("duotoneEarth")
duotone_forest = construct_theme_var("duotoneForest")
duotone_light = construct_theme_var("duotoneLight")
duotone_sea = construct_theme_var("duotoneSea")
duotone_space = construct_theme_var("duotoneSpace")
funky = construct_theme_var("funky")
ghcolors = construct_theme_var("ghcolors")
gruvbox_dark = construct_theme_var("gruvboxDark")
gruvbox_light = construct_theme_var("gruvboxLight")
holi_theme = construct_theme_var("holiTheme")
hopscotch = construct_theme_var("hopscotch")
light = construct_theme_var("oneLight")
lucario = construct_theme_var("lucario")
material_dark = construct_theme_var("materialDark")
material_light = construct_theme_var("materialLight")
material_oceanic = construct_theme_var("materialOceanic")
night_owl = construct_theme_var("nightOwl")
nord = construct_theme_var("nord")
okaidia = construct_theme_var("okaidia")
one_dark = construct_theme_var("oneDark")
one_light = construct_theme_var("oneLight")
pojoaque = construct_theme_var("pojoaque")
prism = construct_theme_var("prism")
shades_of_purple = construct_theme_var("shadesOfPurple")
solarized_dark_atom = construct_theme_var("solarizedDarkAtom")
solarizedlight = construct_theme_var("solarizedlight")
synthwave84 = construct_theme_var("synthwave84")
tomorrow = construct_theme_var("tomorrow")
twilight = construct_theme_var("twilight")
vs = construct_theme_var("vs")
vs_dark = construct_theme_var("vsDark")
vsc_dark_plus = construct_theme_var("vscDarkPlus")
xonokai = construct_theme_var("xonokai")
z_touch = construct_theme_var("zTouch")
class CodeblockNamespace(ComponentNamespace):
themes = Theme
@ -1059,7 +1013,7 @@ class CodeblockNamespace(ComponentNamespace):
*children,
can_copy: Optional[bool] = False,
copy_button: Optional[Union[Component, bool]] = None,
theme: Optional[Union[Any, Var[Any]]] = None,
theme: Optional[Union[Theme, Var[Union[Theme, str]], str]] = None,
language: Optional[
Union[
Literal[

View File

@ -12,31 +12,33 @@ def logo(**props):
Returns:
The logo component.
"""
light_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.6V0.400024H8.96V4.88002H6.72V2.64002H2.24V4.88002H6.72V7.12002H2.24V11.6H0ZM6.72 11.6V7.12002H8.96V11.6H6.72Z" fill="#110F1F"/>
<path d="M11.2 11.6V0.400024H17.92V2.64002H13.44V4.88002H17.92V7.12002H13.44V9.36002H17.92V11.6H11.2Z" fill="#110F1F"/>
<path d="M20.16 11.6V0.400024H26.88V2.64002H22.4V4.88002H26.88V7.12002H22.4V11.6H20.16Z" fill="#110F1F"/>
<path d="M29.12 11.6V0.400024H31.36V9.36002H35.84V11.6H29.12Z" fill="#110F1F"/>
<path d="M38.08 11.6V0.400024H44.8V2.64002H40.32V4.88002H44.8V7.12002H40.32V9.36002H44.8V11.6H38.08Z" fill="#110F1F"/>
<path d="M47.04 4.88002V0.400024H49.28V4.88002H47.04ZM53.76 4.88002V0.400024H56V4.88002H53.76ZM49.28 7.12002V4.88002H53.76V7.12002H49.28ZM47.04 11.6V7.12002H49.28V11.6H47.04ZM53.76 11.6V7.12002H56V11.6H53.76Z" fill="#110F1F"/>
</svg>"""
dark_mode_logo = """<svg width="56" height="12" viewBox="0 0 56 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z" fill="white"/>
<path d="M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z" fill="white"/>
<path d="M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z" fill="white"/>
<path d="M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z" fill="white"/>
<path d="M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z" fill="white"/>
<path d="M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z" fill="white"/>
</svg>"""
def logo_path(d):
return rx.el.svg.path(
d=d,
fill=rx.color_mode_cond("#110F1F", "white"),
)
paths = [
"M0 11.5999V0.399902H8.96V4.8799H6.72V2.6399H2.24V4.8799H6.72V7.1199H2.24V11.5999H0ZM6.72 11.5999V7.1199H8.96V11.5999H6.72Z",
"M11.2 11.5999V0.399902H17.92V2.6399H13.44V4.8799H17.92V7.1199H13.44V9.3599H17.92V11.5999H11.2Z",
"M20.16 11.5999V0.399902H26.88V2.6399H22.4V4.8799H26.88V7.1199H22.4V11.5999H20.16Z",
"M29.12 11.5999V0.399902H31.36V9.3599H35.84V11.5999H29.12Z",
"M38.08 11.5999V0.399902H44.8V2.6399H40.32V4.8799H44.8V7.1199H40.32V9.3599H44.8V11.5999H38.08Z",
"M47.04 4.8799V0.399902H49.28V4.8799H47.04ZM53.76 4.8799V0.399902H56V4.8799H53.76ZM49.28 7.1199V4.8799H53.76V7.1199H49.28ZM47.04 11.5999V7.1199H49.28V11.5999H47.04ZM53.76 11.5999V7.1199H56V11.5999H53.76Z",
]
return rx.center(
rx.link(
rx.hstack(
"Built with ",
rx.color_mode_cond(
rx.html(light_mode_logo),
rx.html(dark_mode_logo),
rx.el.svg(
*[logo_path(d) for d in paths],
width="56",
height="12",
viewBox="0 0 56 12",
fill="none",
xmlns="http://www.w3.org/2000/svg",
),
text_align="center",
align="center",

View File

@ -2,6 +2,7 @@
from reflex import constants
from reflex.utils import imports
from reflex.utils.format import format_library_name
from reflex.utils.serializers import serializer
from reflex.vars import Var, get_unique_variable_name
from reflex.vars.base import VarData, transform
@ -57,12 +58,21 @@ def load_dynamic_serializer():
)
] = None
libs_in_window = [
"react",
"@radix-ui/themes",
]
imports = {}
for lib, names in component._get_all_imports().items():
formatted_lib_name = format_library_name(lib)
if (
not lib.startswith((".", "/"))
and not lib.startswith("http")
and lib != "react"
and all(
formatted_lib_name != lib_in_window
for lib_in_window in libs_in_window
)
):
imports[get_cdn_url(lib)] = names
else:
@ -83,10 +93,16 @@ def load_dynamic_serializer():
)
+ "]"
)
elif 'from "react"' in line:
module_code_lines[ix] = line.replace(
"import ", "const ", 1
).replace(' from "react"', " = window.__reflex.react", 1)
else:
for lib in libs_in_window:
if f'from "{lib}"' in line:
module_code_lines[ix] = (
line.replace("import ", "const ", 1)
.replace(
f' from "{lib}"', f" = window.__reflex['{lib}']", 1
)
.replace(" as ", ": ")
)
if line.startswith("export function"):
module_code_lines[ix] = line.replace(
"export function", "export default function", 1

View File

@ -10,7 +10,7 @@ from jinja2 import Environment
from reflex.components.el.element import Element
from reflex.components.tags.tag import Tag
from reflex.constants import Dirs, EventTriggers
from reflex.event import EventChain, EventHandler
from reflex.event import EventChain, EventHandler, prevent_default
from reflex.utils.imports import ImportDict
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
@ -148,6 +148,9 @@ class Form(BaseHTML):
Returns:
The form component.
"""
if "on_submit" not in props:
props["on_submit"] = prevent_default
if "handle_submit_unique_name" in props:
return super().create(*children, **props)

View File

@ -317,6 +317,42 @@ class Svg(BaseHTML):
xmlns: Var[str]
class Text(BaseHTML):
"""The SVG text component."""
tag = "text"
# The x coordinate of the starting point of the text baseline.
x: Var[Union[str, int]]
# The y coordinate of the starting point of the text baseline.
y: Var[Union[str, int]]
# Shifts the text position horizontally from a previous text element.
dx: Var[Union[str, int]]
# Shifts the text position vertically from a previous text element.
dy: Var[Union[str, int]]
# Rotates orientation of each individual glyph.
rotate: Var[Union[str, int]]
# How the text is stretched or compressed to fit the width defined by the text_length attribute.
length_adjust: Var[str]
# A width that the text should be scaled to fit.
text_length: Var[Union[str, int]]
class Line(BaseHTML):
"""The SVG line component."""
tag = "line"
# The x-axis coordinate of the line starting point.
x1: Var[Union[str, int]]
# The x-axis coordinate of the the line ending point.
x2: Var[Union[str, int]]
# The y-axis coordinate of the line starting point.
y1: Var[Union[str, int]]
# The y-axis coordinate of the the line ending point.
y2: Var[Union[str, int]]
# The total path length, in user units.
path_length: Var[int]
class Circle(BaseHTML):
"""The SVG circle component."""
@ -331,6 +367,22 @@ class Circle(BaseHTML):
path_length: Var[int]
class Ellipse(BaseHTML):
"""The SVG ellipse component."""
tag = "ellipse"
# The x position of the center of the ellipse.
cx: Var[Union[str, int]]
# The y position of the center of the ellipse.
cy: Var[Union[str, int]]
# The radius of the ellipse on the x axis.
rx: Var[Union[str, int]]
# The radius of the ellipse on the y axis.
ry: Var[Union[str, int]]
# The total length for the ellipse's circumference, in user units.
path_length: Var[int]
class Rect(BaseHTML):
"""The SVG rect component."""
@ -394,6 +446,39 @@ class LinearGradient(BaseHTML):
y2: Var[Union[str, int, bool]]
class RadialGradient(BaseHTML):
"""Display the radialGradient element."""
tag = "radialGradient"
# The x coordinate of the end circle of the radial gradient.
cx: Var[Union[str, int, bool]]
# The y coordinate of the end circle of the radial gradient.
cy: Var[Union[str, int, bool]]
# The radius of the start circle of the radial gradient.
fr: Var[Union[str, int, bool]]
# The x coordinate of the start circle of the radial gradient.
fx: Var[Union[str, int, bool]]
# The y coordinate of the start circle of the radial gradient.
fy: Var[Union[str, int, bool]]
# Units for the gradient.
gradient_units: Var[Union[str, bool]]
# Transform applied to the gradient.
gradient_transform: Var[Union[str, bool]]
# The radius of the end circle of the radial gradient.
r: Var[Union[str, int, bool]]
# Method used to spread the gradient.
spread_method: Var[Union[str, bool]]
class Stop(BaseHTML):
"""Display the stop element."""
@ -421,12 +506,16 @@ class Path(BaseHTML):
class SVG(ComponentNamespace):
"""SVG component namespace."""
text = staticmethod(Text.create)
line = staticmethod(Line.create)
circle = staticmethod(Circle.create)
ellipse = staticmethod(Ellipse.create)
rect = staticmethod(Rect.create)
polygon = staticmethod(Polygon.create)
path = staticmethod(Path.create)
stop = staticmethod(Stop.create)
linear_gradient = staticmethod(LinearGradient.create)
radial_gradient = staticmethod(RadialGradient.create)
defs = staticmethod(Defs.create)
__call__ = staticmethod(Svg.create)

View File

@ -1550,6 +1550,242 @@ class Svg(BaseHTML):
"""
...
class Text(BaseHTML):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
x: Optional[Union[Var[Union[int, str]], int, str]] = None,
y: Optional[Union[Var[Union[int, str]], int, str]] = None,
dx: Optional[Union[Var[Union[int, str]], int, str]] = None,
dy: Optional[Union[Var[Union[int, str]], int, str]] = None,
rotate: Optional[Union[Var[Union[int, str]], int, str]] = None,
length_adjust: Optional[Union[Var[str], str]] = None,
text_length: Optional[Union[Var[Union[int, str]], int, str]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
content_editable: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
context_menu: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], bool, int, 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, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props,
) -> "Text":
"""Create the component.
Args:
*children: The children of the component.
x: The x coordinate of the starting point of the text baseline.
y: The y coordinate of the starting point of the text baseline.
dx: Shifts the text position horizontally from a previous text element.
dy: Shifts the text position vertically from a previous text element.
rotate: Rotates orientation of each individual glyph.
length_adjust: How the text is stretched or compressed to fit the width defined by the text_length attribute.
text_length: A width that the text should be scaled to fit.
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
draggable: Defines whether the element can be dragged.
enter_key_hint: Hints what media types the media element is able to play.
hidden: Defines whether the element is hidden.
input_mode: Defines the type of the element.
item_prop: Defines the name of the element for metadata purposes.
lang: Defines the language used in the element.
role: Defines the role of the element.
slot: Assigns a slot in a shadow DOM shadow tree to an element.
spell_check: Defines whether the element may be checked for spelling errors.
tab_index: Defines the position of the current element in the tabbing order.
title: Defines a tooltip for the element.
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.
"""
...
class Line(BaseHTML):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
x1: Optional[Union[Var[Union[int, str]], int, str]] = None,
x2: Optional[Union[Var[Union[int, str]], int, str]] = None,
y1: Optional[Union[Var[Union[int, str]], int, str]] = None,
y2: Optional[Union[Var[Union[int, str]], int, str]] = None,
path_length: Optional[Union[Var[int], int]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
content_editable: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
context_menu: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], bool, int, 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, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props,
) -> "Line":
"""Create the component.
Args:
*children: The children of the component.
x1: The x-axis coordinate of the line starting point.
x2: The x-axis coordinate of the the line ending point.
y1: The y-axis coordinate of the line starting point.
y2: The y-axis coordinate of the the line ending point.
path_length: The total path length, in user units.
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
draggable: Defines whether the element can be dragged.
enter_key_hint: Hints what media types the media element is able to play.
hidden: Defines whether the element is hidden.
input_mode: Defines the type of the element.
item_prop: Defines the name of the element for metadata purposes.
lang: Defines the language used in the element.
role: Defines the role of the element.
slot: Assigns a slot in a shadow DOM shadow tree to an element.
spell_check: Defines whether the element may be checked for spelling errors.
tab_index: Defines the position of the current element in the tabbing order.
title: Defines a tooltip for the element.
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.
"""
...
class Circle(BaseHTML):
@overload
@classmethod
@ -1664,6 +1900,122 @@ class Circle(BaseHTML):
"""
...
class Ellipse(BaseHTML):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
cx: Optional[Union[Var[Union[int, str]], int, str]] = None,
cy: Optional[Union[Var[Union[int, str]], int, str]] = None,
rx: Optional[Union[Var[Union[int, str]], int, str]] = None,
ry: Optional[Union[Var[Union[int, str]], int, str]] = None,
path_length: Optional[Union[Var[int], int]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
content_editable: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
context_menu: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], bool, int, 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, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props,
) -> "Ellipse":
"""Create the component.
Args:
*children: The children of the component.
cx: The x position of the center of the ellipse.
cy: The y position of the center of the ellipse.
rx: The radius of the ellipse on the x axis.
ry: The radius of the ellipse on the y axis.
path_length: The total length for the ellipse's circumference, in user units.
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
draggable: Defines whether the element can be dragged.
enter_key_hint: Hints what media types the media element is able to play.
hidden: Defines whether the element is hidden.
input_mode: Defines the type of the element.
item_prop: Defines the name of the element for metadata purposes.
lang: Defines the language used in the element.
role: Defines the role of the element.
slot: Assigns a slot in a shadow DOM shadow tree to an element.
spell_check: Defines whether the element may be checked for spelling errors.
tab_index: Defines the position of the current element in the tabbing order.
title: Defines a tooltip for the element.
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.
"""
...
class Rect(BaseHTML):
@overload
@classmethod
@ -2120,6 +2472,130 @@ class LinearGradient(BaseHTML):
"""
...
class RadialGradient(BaseHTML):
@overload
@classmethod
def create( # type: ignore
cls,
*children,
cx: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
cy: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
fr: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
fx: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
fy: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
gradient_units: Optional[Union[Var[Union[bool, str]], bool, str]] = None,
gradient_transform: Optional[Union[Var[Union[bool, str]], bool, str]] = None,
r: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spread_method: Optional[Union[Var[Union[bool, str]], bool, str]] = None,
access_key: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
auto_capitalize: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
content_editable: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
context_menu: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
dir: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
draggable: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
enter_key_hint: Optional[
Union[Var[Union[bool, int, str]], bool, int, str]
] = None,
hidden: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
input_mode: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
item_prop: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
lang: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
role: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
slot: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
spell_check: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
tab_index: Optional[Union[Var[Union[bool, int, str]], bool, int, str]] = None,
title: Optional[Union[Var[Union[bool, int, str]], bool, int, 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, str]]] = None,
on_blur: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_click: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_context_menu: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_double_click: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_focus: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mount: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_mouse_down: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_enter: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_leave: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_move: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_out: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_over: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_mouse_up: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
on_scroll: Optional[Union[EventHandler, EventSpec, list, Callable, Var]] = None,
on_unmount: Optional[
Union[EventHandler, EventSpec, list, Callable, Var]
] = None,
**props,
) -> "RadialGradient":
"""Create the component.
Args:
*children: The children of the component.
cx: The x coordinate of the end circle of the radial gradient.
cy: The y coordinate of the end circle of the radial gradient.
fr: The radius of the start circle of the radial gradient.
fx: The x coordinate of the start circle of the radial gradient.
fy: The y coordinate of the start circle of the radial gradient.
gradient_units: Units for the gradient.
gradient_transform: Transform applied to the gradient.
r: The radius of the end circle of the radial gradient.
spread_method: Method used to spread the gradient.
access_key: Provides a hint for generating a keyboard shortcut for the current element.
auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
content_editable: Indicates whether the element's content is editable.
context_menu: Defines the ID of a <menu> element which will serve as the element's context menu.
dir: Defines the text direction. Allowed values are ltr (Left-To-Right) or rtl (Right-To-Left)
draggable: Defines whether the element can be dragged.
enter_key_hint: Hints what media types the media element is able to play.
hidden: Defines whether the element is hidden.
input_mode: Defines the type of the element.
item_prop: Defines the name of the element for metadata purposes.
lang: Defines the language used in the element.
role: Defines the role of the element.
slot: Assigns a slot in a shadow DOM shadow tree to an element.
spell_check: Defines whether the element may be checked for spelling errors.
tab_index: Defines the position of the current element in the tabbing order.
title: Defines a tooltip for the element.
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.
"""
...
class Stop(BaseHTML):
@overload
@classmethod
@ -2345,12 +2821,16 @@ class Path(BaseHTML):
...
class SVG(ComponentNamespace):
text = staticmethod(Text.create)
line = staticmethod(Line.create)
circle = staticmethod(Circle.create)
ellipse = staticmethod(Ellipse.create)
rect = staticmethod(Rect.create)
polygon = staticmethod(Polygon.create)
path = staticmethod(Path.create)
stop = staticmethod(Stop.create)
linear_gradient = staticmethod(LinearGradient.create)
radial_gradient = staticmethod(RadialGradient.create)
defs = staticmethod(Defs.create)
@staticmethod

View File

@ -124,7 +124,8 @@ class DataTable(Gridjs):
if types.is_dataframe(type(self.data)):
# If given a pandas df break up the data and columns
data = serialize(self.data)
assert isinstance(data, dict), "Serialized dataframe should be a dict."
if not isinstance(data, dict):
raise ValueError("Serialized dataframe should be a dict.")
self.columns = LiteralVar.create(data["columns"])
self.data = LiteralVar.create(data["data"])

View File

@ -95,12 +95,16 @@ class Markdown(Component):
*children: The children of the component.
**props: The properties of the component.
Raises:
ValueError: If the children are not valid.
Returns:
The markdown component.
"""
assert (
len(children) == 1 and types._isinstance(children[0], Union[str, Var])
), "Markdown component must have exactly one child containing the markdown source."
if len(children) != 1 or not types._isinstance(children[0], Union[str, Var]):
raise ValueError(
"Markdown component must have exactly one child containing the markdown source."
)
# Update the base component map with the custom component map.
component_map = {**get_base_component_map(), **props.pop("component_map", {})}
@ -147,7 +151,7 @@ class Markdown(Component):
Returns:
The imports for the markdown component.
"""
from reflex.components.datadisplay.code import CodeBlock
from reflex.components.datadisplay.code import CodeBlock, Theme
from reflex.components.radix.themes.typography.code import Code
return [
@ -173,8 +177,8 @@ class Markdown(Component):
component(_MOCK_ARG)._get_all_imports() # type: ignore
for component in self.component_map.values()
],
CodeBlock.create(theme="light")._get_imports(), # type: ignore,
Code.create()._get_imports(), # type: ignore,
CodeBlock.create(theme=Theme.light)._get_imports(),
Code.create()._get_imports(),
]
def get_component(self, tag: str, **props) -> Component:

View File

@ -93,6 +93,9 @@ class Markdown(Component):
custom_attrs: custom attribute
**props: The properties of the component.
Raises:
ValueError: If the children are not valid.
Returns:
The markdown component.
"""

View File

@ -33,7 +33,7 @@ class Stack(Flex):
"""
# Apply the default classname
given_class_name = props.pop("class_name", [])
if isinstance(given_class_name, str):
if not isinstance(given_class_name, list):
given_class_name = [given_class_name]
props["class_name"] = ["rx-Stack", *given_class_name]

View File

@ -17,7 +17,7 @@ from reflex.event import (
from reflex.style import Style, resolved_color_mode
from reflex.utils import format
from reflex.utils.imports import ImportVar
from reflex.utils.serializers import serialize, serializer
from reflex.utils.serializers import serializer
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
@ -281,8 +281,8 @@ class Toaster(Component):
if message == "" and ("title" not in props or "description" not in props):
raise ValueError("Toast message or title or description must be provided.")
if props:
args = serialize(ToastProps(**props)) # type: ignore
toast = f"{toast_command}(`{message}`, {args})"
args = LiteralVar.create(ToastProps(**props))
toast = f"{toast_command}(`{message}`, {str(args)})"
else:
toast = f"{toast_command}(`{message}`)"

View File

@ -114,6 +114,9 @@ class IterTag(Tag):
def render_component(self) -> Component:
"""Render the component.
Raises:
ValueError: If the render function takes more than 2 arguments.
Returns:
The rendered component.
"""
@ -132,7 +135,8 @@ class IterTag(Tag):
component = self.render_fn(arg)
else:
# If the render function takes the index as an argument.
assert len(args) == 2
if len(args) != 2:
raise ValueError("The render function must take 2 arguments.")
component = self.render_fn(arg, index)
# Nested foreach components or cond must be wrapped in fragments.

View File

@ -158,7 +158,7 @@ class Config(Base):
app_name: str
# The log level to use.
loglevel: constants.LogLevel = constants.LogLevel.INFO
loglevel: constants.LogLevel = constants.LogLevel.DEFAULT
# The port to run the frontend on. NOTE: When running in dev mode, the next available port will be used if this is taken.
frontend_port: int = constants.DefaultPorts.FRONTEND_PORT
@ -194,7 +194,7 @@ class Config(Base):
cors_allowed_origins: List[str] = ["*"]
# Tailwind config.
tailwind: Optional[Dict[str, Any]] = {}
tailwind: Optional[Dict[str, Any]] = {"plugins": ["@tailwindcss/typography"]}
# Timeout when launching the gunicorn server. TODO(rename this to backend_timeout?)
timeout: int = 120

View File

@ -173,6 +173,7 @@ class LogLevel(str, Enum):
"""The log levels."""
DEBUG = "debug"
DEFAULT = "default"
INFO = "info"
WARNING = "warning"
ERROR = "error"

View File

@ -54,6 +54,9 @@ class Bun(SimpleNamespace):
# Path of the bunfig file
CONFIG_PATH = "bunfig.toml"
# The environment variable to use the system installed bun.
USE_SYSTEM_VAR = "REFLEX_USE_SYSTEM_BUN"
# FNM config.
class Fnm(SimpleNamespace):
@ -96,6 +99,9 @@ class Node(SimpleNamespace):
# The default path where npm is installed.
NPM_PATH = os.path.join(BIN_PATH, "npm")
# The environment variable to use the system installed node.
USE_SYSTEM_VAR = "REFLEX_USE_SYSTEM_NODE"
class PackageJson(SimpleNamespace):
"""Constants used to build the package.json file."""
@ -115,7 +121,7 @@ class PackageJson(SimpleNamespace):
"@emotion/react": "11.11.1",
"axios": "1.6.0",
"json5": "2.2.3",
"next": "14.0.1",
"next": "14.2.13",
"next-sitemap": "4.1.8",
"next-themes": "0.2.1",
"react": "18.2.0",

View File

@ -18,10 +18,12 @@ from typing import (
get_type_hints,
)
from typing_extensions import get_args, get_origin
from reflex import constants
from reflex.utils import format
from reflex.utils.exceptions import EventFnArgMismatch, EventHandlerArgMismatch
from reflex.utils.types import ArgsSpec
from reflex.utils.types import ArgsSpec, GenericType
from reflex.vars import VarData
from reflex.vars.base import LiteralVar, Var
from reflex.vars.function import FunctionStringVar, FunctionVar
@ -217,7 +219,7 @@ class EventHandler(EventActionsMixin):
raise EventHandlerTypeError(
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
) from e
payload = tuple(zip(fn_args, values, strict=False))
payload = tuple(zip(fn_args, values))
# Return the event spec.
return EventSpec(
@ -310,7 +312,7 @@ class EventSpec(EventActionsMixin):
raise EventHandlerTypeError(
f"Arguments to event handlers must be Vars or JSON-serializable. Got {arg} of type {type(arg)}."
) from e
new_payload = tuple(zip(fn_args, values, strict=False))
new_payload = tuple(zip(fn_args, values))
return self.with_args(self.args + new_payload)
@ -417,7 +419,7 @@ class FileUpload:
on_upload_progress: Optional[Union[EventHandler, Callable]] = None
@staticmethod
def on_upload_progress_args_spec(_prog: Dict[str, Union[int, float, bool]]):
def on_upload_progress_args_spec(_prog: Var[Dict[str, Union[int, float, bool]]]):
"""Args spec for on_upload_progress event handler.
Returns:
@ -910,6 +912,20 @@ def call_event_handler(
)
def unwrap_var_annotation(annotation: GenericType):
"""Unwrap a Var annotation or return it as is if it's not Var[X].
Args:
annotation: The annotation to unwrap.
Returns:
The unwrapped annotation.
"""
if get_origin(annotation) is Var and (args := get_args(annotation)):
return args[0]
return annotation
def parse_args_spec(arg_spec: ArgsSpec):
"""Parse the args provided in the ArgsSpec of an event trigger.
@ -921,20 +937,54 @@ def parse_args_spec(arg_spec: ArgsSpec):
"""
spec = inspect.getfullargspec(arg_spec)
annotations = get_type_hints(arg_spec)
return arg_spec(
*[
Var(f"_{l_arg}").to(annotations.get(l_arg, FrontendEvent))
Var(f"_{l_arg}").to(
unwrap_var_annotation(annotations.get(l_arg, FrontendEvent))
)
for l_arg in spec.args
]
)
def check_fn_match_arg_spec(fn: Callable, arg_spec: ArgsSpec) -> List[Var]:
"""Ensures that the function signature matches the passed argument specification
or raises an EventFnArgMismatch if they do not.
Args:
fn: The function to be validated.
arg_spec: The argument specification for the event trigger.
Returns:
The parsed arguments from the argument specification.
Raises:
EventFnArgMismatch: Raised if the number of mandatory arguments do not match
"""
fn_args = inspect.getfullargspec(fn).args
fn_defaults_args = inspect.getfullargspec(fn).defaults
n_fn_args = len(fn_args)
n_fn_defaults_args = len(fn_defaults_args) if fn_defaults_args else 0
if isinstance(fn, types.MethodType):
n_fn_args -= 1 # subtract 1 for bound self arg
parsed_args = parse_args_spec(arg_spec)
if not (n_fn_args - n_fn_defaults_args <= len(parsed_args) <= n_fn_args):
raise EventFnArgMismatch(
"The number of mandatory arguments accepted by "
f"{fn} ({n_fn_args - n_fn_defaults_args}) "
"does not match the arguments passed by the event trigger: "
f"{[str(v) for v in parsed_args]}\n"
"See https://reflex.dev/docs/events/event-arguments/"
)
return parsed_args
def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
"""Call a function to a list of event specs.
The function should return a single EventSpec, a list of EventSpecs, or a
single Var. The function signature must match the passed arg_spec or
EventFnArgsMismatch will be raised.
single Var.
Args:
fn: The function to call.
@ -944,7 +994,6 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
The event specs from calling the function or a Var.
Raises:
EventFnArgMismatch: If the function signature doesn't match the arg spec.
EventHandlerValueError: If the lambda returns an unusable value.
"""
# Import here to avoid circular imports.
@ -952,19 +1001,7 @@ def call_event_fn(fn: Callable, arg_spec: ArgsSpec) -> list[EventSpec] | Var:
from reflex.utils.exceptions import EventHandlerValueError
# Check that fn signature matches arg_spec
fn_args = inspect.getfullargspec(fn).args
n_fn_args = len(fn_args)
if isinstance(fn, types.MethodType):
n_fn_args -= 1 # subtract 1 for bound self arg
parsed_args = parse_args_spec(arg_spec)
if len(parsed_args) != n_fn_args:
raise EventFnArgMismatch(
"The number of arguments accepted by "
f"{fn} ({n_fn_args}) "
"does not match the arguments passed by the event trigger: "
f"{[str(v) for v in parsed_args]}\n"
"See https://reflex.dev/docs/events/event-arguments/"
)
parsed_args = check_fn_match_arg_spec(fn, arg_spec)
# Call the function with the parsed args.
out = fn(*parsed_args)
@ -1025,6 +1062,9 @@ def fix_events(
token: The user token.
router_data: The optional router data to set in the event.
Raises:
ValueError: If the event type is not what was expected.
Returns:
The fixed events.
"""
@ -1048,7 +1088,8 @@ def fix_events(
# Otherwise, create an event from the event spec.
if isinstance(e, EventHandler):
e = e()
assert isinstance(e, EventSpec), f"Unexpected event type, {type(e)}."
if not isinstance(e, EventSpec):
raise ValueError(f"Unexpected event type, {type(e)}.")
name = format.format_event_handler(e.handler)
payload = {k._js_expr: v._decode() for k, v in e.args} # type: ignore

View File

@ -24,6 +24,7 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
Raises:
FileNotFoundError: If the file does not exist.
ValueError: If the module is None.
Returns:
The relative URL to the copied asset.
@ -31,7 +32,8 @@ def asset(relative_filename: str, subfolder: Optional[str] = None) -> str:
# Determine the file by which the asset is exposed.
calling_file = inspect.stack()[1].filename
module = inspect.getmodule(inspect.stack()[1][0])
assert module is not None
if module is None:
raise ValueError("Module is None")
caller_module_path = module.__name__.replace(".", "/")
subfolder = f"{caller_module_path}/{subfolder}" if subfolder else caller_module_path

View File

@ -3,6 +3,7 @@
from __future__ import annotations
import dataclasses
import re
import sys
from typing import Any, Callable, Union
@ -90,12 +91,16 @@ class ClientStateVar(Var):
default: The default value of the variable.
global_ref: Whether the state should be accessible in any Component and on the backend.
Raises:
ValueError: If the var_name is not a string.
Returns:
ClientStateVar
"""
if var_name is None:
var_name = get_unique_variable_name()
assert isinstance(var_name, str), "var_name must be a string."
if not isinstance(var_name, str):
raise ValueError("var_name must be a string.")
if default is NoValue:
default_var = Var(_js_expr="")
elif not isinstance(default, Var):
@ -174,15 +179,15 @@ class ClientStateVar(Var):
else self._setter_name
)
if value is not NoValue:
import re
# This is a hack to make it work like an EventSpec taking an arg
value_str = str(LiteralVar.create(value))
if value_str.startswith("_"):
# remove patterns of ["*"] from the value_str using regex
arg = re.sub(r"\[\".*\"\]", "", value_str)
setter = f"({arg}) => {setter}({str(value)})"
setter = f"(({arg}) => {setter}({value_str}))"
else:
setter = f"(() => {setter}({value_str}))"
return Var(
_js_expr=setter,
_var_data=VarData(imports=_refs_import if self._global_ref else {}),

View File

@ -12,10 +12,12 @@ async def run_in_thread(func) -> Any:
Args:
func (callable): The non-async function to run.
Raises:
ValueError: If the function is an async function.
Returns:
Any: The return value of the function.
"""
assert not asyncio.coroutines.iscoroutinefunction(
func
), "func must be a non-async function"
if asyncio.coroutines.iscoroutinefunction(func):
raise ValueError("func must be a non-async function")
return await asyncio.get_event_loop().run_in_executor(None, func)

View File

@ -9,7 +9,6 @@ from reflex import constants
from reflex.event import Event, get_hydrate_event
from reflex.middleware.middleware import Middleware
from reflex.state import BaseState, StateUpdate
from reflex.utils import format
if TYPE_CHECKING:
from reflex.app import App
@ -43,7 +42,7 @@ class HydrateMiddleware(Middleware):
setattr(state, constants.CompileVars.IS_HYDRATED, False)
# Get the initial state.
delta = format.format_state(state.dict())
delta = state.dict()
# since a full dict was captured, clean any dirtiness
state._clean()

View File

@ -22,7 +22,7 @@ from reflex import constants
from reflex.base import Base
from reflex.config import get_config
from reflex.utils import console
from reflex.utils.compat import sqlmodel
from reflex.utils.compat import sqlmodel, sqlmodel_field_has_primary_key
def get_engine(url: str | None = None) -> sqlalchemy.engine.Engine:
@ -166,8 +166,7 @@ class Model(Base, sqlmodel.SQLModel): # pyright: ignore [reportGeneralTypeIssue
non_default_primary_key_fields = [
field_name
for field_name, field in cls.__fields__.items()
if field_name != "id"
and getattr(field.field_info, "primary_key", None) is True
if field_name != "id" and sqlmodel_field_has_primary_key(field)
]
if non_default_primary_key_fields:
cls.__fields__.pop("id", None)

View File

@ -63,3 +63,22 @@ def page(
return render_fn
return decorator
def get_decorated_pages(omit_implicit_routes=True) -> list[dict[str, Any]]:
"""Get the decorated pages.
Args:
omit_implicit_routes: Whether to omit pages where the route will be implicitely guessed later.
Returns:
The decorated pages.
"""
return sorted(
[
page_data
for _, page_data in DECORATED_PAGES[get_config().app_name]
if not omit_implicit_routes or "route" in page_data
],
key=lambda x: x.get("route", ""),
)

View File

@ -15,6 +15,7 @@ from reflex_cli.utils import dependency
from reflex import constants
from reflex.config import get_config
from reflex.constants.base import LogLevel
from reflex.custom_components.custom_components import custom_components_cli
from reflex.state import reset_disk_state_manager
from reflex.utils import console, redir, telemetry
@ -229,7 +230,8 @@ def _run(
exec.run_frontend_prod,
exec.run_backend_prod,
)
assert setup_frontend and frontend_cmd and backend_cmd, "Invalid env"
if not setup_frontend or not frontend_cmd or not backend_cmd:
raise ValueError("Invalid env")
# Post a telemetry event.
telemetry.send(f"run-{env.value}")
@ -245,15 +247,30 @@ def _run(
setup_frontend(Path.cwd())
commands.append((frontend_cmd, Path.cwd(), frontend_port, backend))
# If no loglevel is specified, set the subprocesses loglevel to WARNING.
subprocesses_loglevel = (
loglevel if loglevel != LogLevel.DEFAULT else LogLevel.WARNING
)
# In prod mode, run the backend on a separate thread.
if backend and env == constants.Env.PROD:
commands.append((backend_cmd, backend_host, backend_port))
commands.append(
(
backend_cmd,
backend_host,
backend_port,
subprocesses_loglevel,
frontend,
)
)
# Start the frontend and backend.
with processes.run_concurrently_context(*commands):
# In dev mode, run the backend on the main thread.
if backend and env == constants.Env.DEV:
backend_cmd(backend_host, int(backend_port))
backend_cmd(
backend_host, int(backend_port), subprocesses_loglevel, frontend
)
# The windows uvicorn bug workaround
# https://github.com/reflex-dev/reflex/issues/2335
if constants.IS_WINDOWS and exec.frontend_process:

View File

@ -12,6 +12,7 @@ import os
import uuid
from abc import ABC, abstractmethod
from collections import defaultdict
from hashlib import md5
from pathlib import Path
from types import FunctionType, MethodType
from typing import (
@ -29,10 +30,12 @@ from typing import (
Type,
Union,
cast,
get_type_hints,
)
import dill
from sqlalchemy.orm import DeclarativeBase
from typing_extensions import Self
from reflex.config import get_config
from reflex.vars.base import (
@ -41,6 +44,7 @@ from reflex.vars.base import (
Var,
computed_var,
dispatch,
get_unique_variable_name,
is_computed_var,
)
@ -72,7 +76,7 @@ from reflex.utils.exceptions import (
LockExpiredError,
)
from reflex.utils.exec import is_testing_env
from reflex.utils.serializers import SerializedType, serialize, serializer
from reflex.utils.serializers import serializer
from reflex.utils.types import override
from reflex.vars import VarData
@ -572,6 +576,15 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
for name, value in cls.__dict__.items()
if types.is_backend_base_variable(name, cls)
}
# Add annotated backend vars that do not have a default value.
new_backend_vars.update(
{
name: Var("", _var_type=annotation_value).get_default_value()
for name, annotation_value in get_type_hints(cls).items()
if name not in new_backend_vars
and types.is_backend_base_variable(name, cls)
}
)
cls.backend_vars = {
**cls.inherited_backend_vars,
@ -684,6 +697,36 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
and hasattr(value, "__code__")
)
@classmethod
def _evaluate(cls, f: Callable[[Self], Any]) -> Var:
"""Evaluate a function to a ComputedVar. Experimental.
Args:
f: The function to evaluate.
Returns:
The ComputedVar.
"""
console.warn(
"The _evaluate method is experimental and may be removed in future versions."
)
from reflex.components.base.fragment import fragment
from reflex.components.component import Component
unique_var_name = get_unique_variable_name()
@computed_var(_js_expr=unique_var_name, return_type=Component)
def computed_var_func(state: Self):
return fragment(f(state))
setattr(cls, unique_var_name, computed_var_func)
cls.computed_vars[unique_var_name] = computed_var_func
cls.vars[unique_var_name] = computed_var_func
cls._update_substate_inherited_vars({unique_var_name: computed_var_func})
cls._always_dirty_computed_vars.add(unique_var_name)
return getattr(cls, unique_var_name)
@classmethod
def _mixins(cls) -> List[Type]:
"""Get the mixin classes of the state.
@ -827,6 +870,9 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
def get_parent_state(cls) -> Type[BaseState] | None:
"""Get the parent state.
Raises:
ValueError: If more than one parent state is found.
Returns:
The parent state.
"""
@ -835,9 +881,8 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
for base in cls.__bases__
if issubclass(base, BaseState) and base is not BaseState and not base._mixin
]
assert (
len(parent_states) < 2
), f"Only one parent state is allowed {parent_states}."
if len(parent_states) >= 2:
raise ValueError(f"Only one parent state is allowed {parent_states}.")
return parent_states[0] if len(parent_states) == 1 else None # type: ignore
@classmethod
@ -1316,7 +1361,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
for part1, part2 in zip(
cls.get_full_name().split("."),
other.get_full_name().split("."),
strict=False,
):
if part1 != part2:
break
@ -1790,9 +1834,6 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
for substate in self.dirty_substates.union(self._always_dirty_substates):
delta.update(substates[substate].get_delta())
# Format the delta.
delta = format.format_state(delta)
# Return the delta.
return delta
@ -2433,7 +2474,7 @@ class StateUpdate:
Returns:
The state update as a JSON string.
"""
return format.json_dumps(dataclasses.asdict(self))
return format.json_dumps(self)
class StateManager(Base, ABC):
@ -2592,16 +2633,24 @@ def _serialize_type(type_: Any) -> str:
return f"{type_.__module__}.{type_.__qualname__}"
def is_serializable(value: Any) -> bool:
"""Check if a value is serializable.
Args:
value: The value to check.
Returns:
Whether the value is serializable.
"""
try:
return bool(dill.dumps(value))
except Exception:
return False
def state_to_schema(
state: BaseState,
) -> List[
Tuple[
str,
str,
Any,
Union[bool, None],
]
]:
) -> List[Tuple[str, str, Any, Union[bool, None], Any]]:
"""Convert a state to a schema.
Args:
@ -2621,6 +2670,7 @@ def state_to_schema(
if isinstance(model_field.required, bool)
else None
),
(model_field.default if is_serializable(model_field.default) else None),
)
for field_name, model_field in state.__fields__.items()
)
@ -2705,7 +2755,9 @@ class StateManagerDisk(StateManager):
Returns:
The path for the token.
"""
return (self.states_directory / f"{token}.pkl").absolute()
return (
self.states_directory / f"{md5(token.encode()).hexdigest()}.pkl"
).absolute()
async def load_state(self, token: str, root_state: BaseState) -> BaseState:
"""Load a state object based on the provided token.
@ -3649,22 +3701,16 @@ class MutableProxy(wrapt.ObjectProxy):
@serializer
def serialize_mutable_proxy(mp: MutableProxy) -> SerializedType:
"""Serialize the wrapped value of a MutableProxy.
def serialize_mutable_proxy(mp: MutableProxy):
"""Return the wrapped value of a MutableProxy.
Args:
mp: The MutableProxy to serialize.
Returns:
The serialized wrapped object.
Raises:
ValueError: when the wrapped object is not serializable.
The wrapped object.
"""
value = serialize(mp.__wrapped__)
if value is None:
raise ValueError(f"Cannot serialize {type(mp.__wrapped__)}")
return value
return mp.__wrapped__
class ImmutableMutableProxy(MutableProxy):

View File

@ -13,6 +13,7 @@ from reflex.utils.imports import ImportVar
from reflex.vars import VarData
from reflex.vars.base import CallableVar, LiteralVar, Var
from reflex.vars.function import FunctionVar
from reflex.vars.object import ObjectVar
SYSTEM_COLOR_MODE: str = "system"
LIGHT_COLOR_MODE: str = "light"
@ -188,7 +189,16 @@ def convert(
out[k] = return_value
for key, value in style_dict.items():
keys = format_style_key(key)
keys = (
format_style_key(key)
if not isinstance(value, (dict, ObjectVar))
or (
isinstance(value, Breakpoints)
and all(not isinstance(v, dict) for v in value.values())
)
else (key,)
)
if isinstance(value, Var):
return_val = value
new_var_data = value._get_all_var_data()
@ -286,7 +296,7 @@ def _format_emotion_style_pseudo_selector(key: str) -> str:
"""Format a pseudo selector for emotion CSS-in-JS.
Args:
key: Underscore-prefixed or colon-prefixed pseudo selector key (_hover).
key: Underscore-prefixed or colon-prefixed pseudo selector key (_hover/:hover).
Returns:
A self-referential pseudo selector key (&:hover).

View File

@ -340,6 +340,9 @@ class AppHarness:
This is necessary when the backend is restarted and the state manager is a
StateManagerRedis instance.
Raises:
RuntimeError: when the state manager cannot be reset
"""
if (
self.app_instance is not None
@ -354,7 +357,8 @@ class AppHarness:
self.app_instance._state_manager = StateManagerRedis.create(
state=self.app_instance.state,
)
assert isinstance(self.app_instance.state_manager, StateManagerRedis)
if not isinstance(self.app_instance.state_manager, StateManagerRedis):
raise RuntimeError("Failed to reset state manager.")
def _start_frontend(self):
# Set up the frontend.
@ -787,13 +791,13 @@ class AppHarness:
Raises:
RuntimeError: when the app hasn't started running
TimeoutError: when the timeout expires before any states are seen
ValueError: when the state_manager is not a memory state manager
"""
if self.app_instance is None:
raise RuntimeError("App is not running.")
state_manager = self.app_instance.state_manager
assert isinstance(
state_manager, (StateManagerMemory, StateManagerDisk)
), "Only works with memory state manager"
if not isinstance(state_manager, (StateManagerMemory, StateManagerDisk)):
raise ValueError("Only works with memory or disk state manager")
if not self._poll_for(
target=lambda: state_manager.states,
timeout=timeout,

View File

@ -69,3 +69,21 @@ def pydantic_v1_patch():
with pydantic_v1_patch():
import sqlmodel as sqlmodel
def sqlmodel_field_has_primary_key(field) -> bool:
"""Determines if a field is a priamary.
Args:
field: a rx.model field
Returns:
If field is a primary key (Bool)
"""
if getattr(field.field_info, "primary_key", None) is True:
return True
if getattr(field.field_info, "sa_column", None) is None:
return False
if getattr(field.field_info.sa_column, "primary_key", None) is True:
return True
return False

View File

@ -58,7 +58,7 @@ def debug(msg: str, **kwargs):
kwargs: Keyword arguments to pass to the print function.
"""
if is_debug():
msg_ = f"[blue]Debug: {msg}[/blue]"
msg_ = f"[purple]Debug: {msg}[/purple]"
if progress := kwargs.pop("progress", None):
progress.console.print(msg_, **kwargs)
else:

View File

@ -111,3 +111,7 @@ class GeneratedCodeHasNoFunctionDefs(ReflexError):
class PrimitiveUnserializableToJSON(ReflexError, ValueError):
"""Raised when a primitive type is unserializable to JSON. Usually with NaN and Infinity."""
class InvalidLifespanTaskType(ReflexError, TypeError):
"""Raised when an invalid task type is registered as a lifespan task."""

View File

@ -16,6 +16,7 @@ import psutil
from reflex import constants
from reflex.config import get_config
from reflex.constants.base import LogLevel
from reflex.utils import console, path_ops
from reflex.utils.prerequisites import get_web_dir
@ -60,6 +61,13 @@ def kill(proc_pid: int):
process.kill()
def notify_backend():
"""Output a string notifying where the backend is running."""
console.print(
f"Backend running at: [bold green]http://0.0.0.0:{get_config().backend_port}[/bold green]"
)
# run_process_and_launch_url is assumed to be used
# only to launch the frontend
# If this is not the case, might have to change the logic
@ -103,9 +111,7 @@ def run_process_and_launch_url(run_command: list[str], backend_present=True):
f"App running at: [bold green]{url}[/bold green]{' (Frontend-only mode)' if not backend_present else ''}"
)
if backend_present:
console.print(
f"Backend running at: [bold green]http://0.0.0.0:{get_config().backend_port}[/bold green]"
)
notify_backend()
first_run = False
else:
console.print("New packages detected: Updating app...")
@ -172,13 +178,69 @@ def run_frontend_prod(root: Path, port: str, backend_present=True):
)
def should_use_granian():
"""Whether to use Granian for backend.
Returns:
True if Granian should be used.
"""
return os.getenv("REFLEX_USE_GRANIAN", "0") == "1"
def get_app_module():
"""Get the app module for the backend.
Returns:
The app module for the backend.
"""
return f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
def get_granian_target():
"""Get the Granian target for the backend.
Returns:
The Granian target for the backend.
"""
import reflex
app_module_path = Path(reflex.__file__).parent / "app_module_for_backend.py"
return f"{str(app_module_path)}:{constants.CompileVars.APP}.{constants.CompileVars.API}"
def run_backend(
host: str,
port: int,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
frontend_present: bool = False,
):
"""Run the backend.
Args:
host: The app host
port: The app port
loglevel: The log level.
frontend_present: Whether the frontend is present.
"""
web_dir = get_web_dir()
# Create a .nocompile file to skip compile for backend.
if web_dir.exists():
(web_dir / constants.NOCOMPILE_FILE).touch()
if not frontend_present:
notify_backend()
# Run the backend in development mode.
if should_use_granian():
run_granian_backend(host, port, loglevel)
else:
run_uvicorn_backend(host, port, loglevel)
def run_uvicorn_backend(host, port, loglevel: LogLevel):
"""Run the backend in development mode using Uvicorn.
Args:
host: The app host
port: The app port
@ -186,22 +248,55 @@ def run_backend(
"""
import uvicorn
config = get_config()
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
web_dir = get_web_dir()
# Create a .nocompile file to skip compile for backend.
if web_dir.exists():
(web_dir / constants.NOCOMPILE_FILE).touch()
# Run the backend in development mode.
uvicorn.run(
app=f"{app_module}.{constants.CompileVars.API}",
app=f"{get_app_module()}.{constants.CompileVars.API}",
host=host,
port=port,
log_level=loglevel.value,
reload=True,
reload_dirs=[config.app_name],
reload_dirs=[get_config().app_name],
)
def run_granian_backend(host, port, loglevel: LogLevel):
"""Run the backend in development mode using Granian.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
console.debug("Using Granian for backend")
try:
from granian import Granian # type: ignore
from granian.constants import Interfaces # type: ignore
from granian.log import LogLevels # type: ignore
Granian(
target=get_granian_target(),
address=host,
port=port,
interface=Interfaces.ASGI,
log_level=LogLevels(loglevel.value),
reload=True,
reload_paths=[Path(get_config().app_name)],
reload_ignore_dirs=[".web"],
).serve()
except ImportError:
console.error(
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
)
os._exit(1)
def _get_backend_workers():
from reflex.utils import processes
config = get_config()
return (
processes.get_num_workers()
if not config.gunicorn_workers
else config.gunicorn_workers
)
@ -209,9 +304,28 @@ def run_backend_prod(
host: str,
port: int,
loglevel: constants.LogLevel = constants.LogLevel.ERROR,
frontend_present: bool = False,
):
"""Run the backend.
Args:
host: The app host
port: The app port
loglevel: The log level.
frontend_present: Whether the frontend is present.
"""
if not frontend_present:
notify_backend()
if should_use_granian():
run_granian_backend_prod(host, port, loglevel)
else:
run_uvicorn_backend_prod(host, port, loglevel)
def run_uvicorn_backend_prod(host, port, loglevel):
"""Run the backend in production mode using Uvicorn.
Args:
host: The app host
port: The app port
@ -220,14 +334,11 @@ def run_backend_prod(
from reflex.utils import processes
config = get_config()
num_workers = (
processes.get_num_workers()
if not config.gunicorn_workers
else config.gunicorn_workers
)
app_module = get_app_module()
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --preload --timeout {config.timeout} --log-level critical".split()
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --timeout-keep-alive {config.timeout}".split()
app_module = f"reflex.app_module_for_backend:{constants.CompileVars.APP}"
command = (
[
*RUN_BACKEND_PROD_WINDOWS,
@ -243,7 +354,7 @@ def run_backend_prod(
"--bind",
f"{host}:{port}",
"--threads",
str(num_workers),
str(_get_backend_workers()),
f"{app_module}()",
]
)
@ -252,7 +363,7 @@ def run_backend_prod(
"--log-level",
loglevel.value,
"--workers",
str(num_workers),
str(_get_backend_workers()),
]
processes.new_process(
command,
@ -262,6 +373,47 @@ def run_backend_prod(
)
def run_granian_backend_prod(host, port, loglevel):
"""Run the backend in production mode using Granian.
Args:
host: The app host
port: The app port
loglevel: The log level.
"""
from reflex.utils import processes
try:
from granian.constants import Interfaces # type: ignore
command = [
"granian",
"--workers",
str(_get_backend_workers()),
"--log-level",
"critical",
"--host",
host,
"--port",
str(port),
"--interface",
str(Interfaces.ASGI),
get_granian_target(),
]
processes.new_process(
command,
run=True,
show_logs=True,
env={
constants.SKIP_COMPILE_ENV_VAR: "yes"
}, # skip compile for prod backend
)
except ImportError:
console.error(
'InstallError: REFLEX_USE_GRANIAN is set but `granian` is not installed. (run `pip install "granian>=1.6.0"`)'
)
def output_system_info():
"""Show system information if the loglevel is in DEBUG."""
if console._LOG_LEVEL > constants.LogLevel.DEBUG:

View File

@ -9,7 +9,7 @@ import re
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Union
from reflex import constants
from reflex.utils import exceptions, types
from reflex.utils import exceptions
from reflex.utils.console import deprecate
if TYPE_CHECKING:
@ -345,6 +345,7 @@ def format_prop(
Raises:
exceptions.InvalidStylePropError: If the style prop value is not a valid type.
TypeError: If the prop is not valid.
ValueError: If the prop is not a string.
"""
# import here to avoid circular import.
from reflex.event import EventChain
@ -391,7 +392,8 @@ def format_prop(
raise TypeError(f"Could not format prop: {prop} of type {type(prop)}") from e
# Wrap the variable in braces.
assert isinstance(prop, str), "The prop must be a string."
if not isinstance(prop, str):
raise ValueError(f"Invalid prop: {prop}. Expected a string.")
return wrap(prop, "{", check_first=False)
@ -624,48 +626,6 @@ def format_query_params(router_data: dict[str, Any]) -> dict[str, str]:
return {k.replace("-", "_"): v for k, v in params.items()}
def format_state(value: Any, key: Optional[str] = None) -> Any:
"""Recursively format values in the given state.
Args:
value: The state to format.
key: The key associated with the value (optional).
Returns:
The formatted state.
Raises:
TypeError: If the given value is not a valid state.
"""
from reflex.utils import serializers
# Handle dicts.
if isinstance(value, dict):
return {k: format_state(v, k) for k, v in value.items()}
# Handle lists, sets, typles.
if isinstance(value, types.StateIterBases):
return [format_state(v) for v in value]
# Return state vars as is.
if isinstance(value, types.StateBases):
return value
# Serialize the value.
serialized = serializers.serialize(value)
if serialized is not None:
return serialized
if key is None:
raise TypeError(
f"No JSON serializer found for var {value} of type {type(value)}."
)
else:
raise TypeError(
f"No JSON serializer found for State Var '{key}' of value {value} of type {type(value)}."
)
def format_state_name(state_name: str) -> str:
"""Format a state name, replacing dots with double underscore.

View File

@ -129,6 +129,41 @@ def which(program: str | Path) -> str | Path | None:
return shutil.which(str(program))
def use_system_install(var_name: str) -> bool:
"""Check if the system install should be used.
Args:
var_name: The name of the environment variable.
Raises:
ValueError: If the variable name is invalid.
Returns:
Whether the associated env var should use the system install.
"""
if not var_name.startswith("REFLEX_USE_SYSTEM_"):
raise ValueError("Invalid system install variable name.")
return os.getenv(var_name, "").lower() in ["true", "1", "yes"]
def use_system_node() -> bool:
"""Check if the system node should be used.
Returns:
Whether the system node should be used.
"""
return use_system_install(constants.Node.USE_SYSTEM_VAR)
def use_system_bun() -> bool:
"""Check if the system bun should be used.
Returns:
Whether the system bun should be used.
"""
return use_system_install(constants.Bun.USE_SYSTEM_VAR)
def get_node_bin_path() -> str | None:
"""Get the node binary dir path.
@ -149,7 +184,7 @@ def get_node_path() -> str | None:
The path to the node binary file.
"""
node_path = Path(constants.Node.PATH)
if not node_path.exists():
if use_system_node() or not node_path.exists():
return str(which("node"))
return str(node_path)

View File

@ -74,6 +74,18 @@ def get_web_dir() -> Path:
return workdir
def _python_version_check():
"""Emit deprecation warning for deprecated python versions."""
# Check for end-of-life python versions.
if sys.version_info < (3, 10):
console.deprecate(
feature_name="Support for Python 3.9 and older",
reason="please upgrade to Python 3.10 or newer",
deprecation_version="0.6.0",
removal_version="0.7.0",
)
def check_latest_package_version(package_name: str):
"""Check if the latest version of the package is installed.
@ -86,15 +98,16 @@ def check_latest_package_version(package_name: str):
url = f"https://pypi.org/pypi/{package_name}/json"
response = net.get(url)
latest_version = response.json()["info"]["version"]
if (
version.parse(current_version) < version.parse(latest_version)
and not get_or_set_last_reflex_version_check_datetime()
):
# only show a warning when the host version is outdated and
# the last_version_check_datetime is not set in reflex.json
if get_or_set_last_reflex_version_check_datetime():
# Versions were already checked and saved in reflex.json, no need to warn again
return
if version.parse(current_version) < version.parse(latest_version):
# Show a warning when the host version is older than PyPI version
console.warn(
f"Your version ({current_version}) of {package_name} is out of date. Upgrade to {latest_version} with 'pip install {package_name} --upgrade'"
)
# Check for depreacted python versions
_python_version_check()
except Exception:
pass
@ -130,7 +143,7 @@ def check_node_version() -> bool:
# Compare the version numbers
return (
current_version >= version.parse(constants.Node.MIN_VERSION)
if constants.IS_WINDOWS
if constants.IS_WINDOWS or path_ops.use_system_node()
else current_version == version.parse(constants.Node.VERSION)
)
return False
@ -291,7 +304,7 @@ def get_compiled_app(reload: bool = False, export: bool = False) -> ModuleType:
"""
app_module = get_app(reload=reload)
app = getattr(app_module, constants.CompileVars.APP)
# For py3.8 and 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).
app._apply_decorated_pages()
app._compile(export=export)
@ -1021,6 +1034,8 @@ def validate_bun():
# if a custom bun path is provided, make sure its valid
# This is specific to non-FHS OS
bun_path = get_config().bun_path
if path_ops.use_system_bun():
bun_path = path_ops.which("bun")
if bun_path != constants.Bun.DEFAULT_PATH:
console.info(f"Using custom Bun path: {bun_path}")
bun_version = get_bun_version()
@ -1481,13 +1496,20 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
main_module_path = Path(app_name, app_name + constants.Ext.PY)
main_module_code = main_module_path.read_text()
main_module_path.write_text(
re.sub(
main_module_code = re.sub(
r"def index\(\).*:\n([^\n]\s+.*\n+)+",
replace_content,
main_module_code,
)
# Make the app use light mode until flexgen enforces the conversion of
# tailwind colors to radix colors.
main_module_code = re.sub(
r"app\s*=\s*rx\.App\(\s*\)",
'app = rx.App(theme=rx.theme(color_mode="light"))',
main_module_code,
)
main_module_path.write_text(main_module_code)
def format_address_width(address_width) -> int | None:

View File

@ -12,7 +12,6 @@ from pathlib import Path
from typing import (
Any,
Callable,
Dict,
List,
Literal,
Optional,
@ -126,7 +125,8 @@ def serialize(
# If there is no serializer, return None.
if serializer is None:
if dataclasses.is_dataclass(value) and not isinstance(value, type):
return serialize(dataclasses.asdict(value))
return {k.name: getattr(value, k.name) for k in dataclasses.fields(value)}
if get_type:
return None, None
return None
@ -214,32 +214,6 @@ def serialize_type(value: type) -> str:
return value.__name__
@serializer
def serialize_str(value: str) -> str:
"""Serialize a string.
Args:
value: The string to serialize.
Returns:
The serialized string.
"""
return value
@serializer
def serialize_primitive(value: Union[bool, int, float, None]):
"""Serialize a primitive type.
Args:
value: The number/bool/None to serialize.
Returns:
The serialized number/bool/None.
"""
return value
@serializer
def serialize_base(value: Base) -> dict:
"""Serialize a Base instance.
@ -250,33 +224,20 @@ def serialize_base(value: Base) -> dict:
Returns:
The serialized Base.
"""
return {k: serialize(v) for k, v in value.dict().items() if not callable(v)}
return {k: v for k, v in value.dict().items() if not callable(v)}
@serializer
def serialize_list(value: Union[List, Tuple, Set]) -> list:
"""Serialize a list to a JSON string.
def serialize_set(value: Set) -> list:
"""Serialize a set to a JSON serializable list.
Args:
value: The list to serialize.
value: The set to serialize.
Returns:
The serialized list.
"""
return [serialize(item) for item in value]
@serializer
def serialize_dict(prop: Dict[str, Any]) -> dict:
"""Serialize a dictionary to a JSON string.
Args:
prop: The dictionary to serialize.
Returns:
The serialized dictionary.
"""
return {k: serialize(v) for k, v in prop.items()}
return list(value)
@serializer(to=str)

View File

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

View File

@ -9,6 +9,7 @@ import sys
import types
from functools import cached_property, lru_cache, wraps
from typing import (
TYPE_CHECKING,
Any,
Callable,
ClassVar,
@ -96,8 +97,22 @@ PrimitiveType = Union[int, float, bool, str, list, dict, set, tuple]
StateVar = Union[PrimitiveType, Base, None]
StateIterVar = Union[list, set, tuple]
# ArgsSpec = Callable[[Var], list[Var]]
ArgsSpec = Callable
if TYPE_CHECKING:
from reflex.vars.base import Var
# ArgsSpec = Callable[[Var], list[Var]]
ArgsSpec = (
Callable[[], List[Var]]
| Callable[[Var], List[Var]]
| Callable[[Var, Var], List[Var]]
| Callable[[Var, Var, Var], List[Var]]
| Callable[[Var, Var, Var, Var], List[Var]]
| Callable[[Var, Var, Var, Var, Var], List[Var]]
| Callable[[Var, Var, Var, Var, Var, Var], List[Var]]
| Callable[[Var, Var, Var, Var, Var, Var, Var], List[Var]]
)
else:
ArgsSpec = Callable[..., List[Any]]
PrimitiveToAnnotation = {
@ -632,7 +647,7 @@ def validate_parameter_literals(func):
annotations = {param[0]: param[1].annotation for param in func_params}
# validate args
for param, arg in zip(annotations, args, strict=False):
for param, arg in zip(annotations, args):
if annotations[param] is inspect.Parameter.empty:
continue
validate_literal(param, arg, annotations[param], func.__name__)

View File

@ -1141,7 +1141,7 @@ def serialize_literal(value: LiteralVar):
Returns:
The serialized Literal.
"""
return serializers.serialize(value._var_value)
return value._var_value
P = ParamSpec("P")
@ -1559,8 +1559,9 @@ class ComputedVar(Var[RETURN_TYPE]):
Raises:
TypeError: If the computed var dependencies are not Var instances or var names.
"""
hints = get_type_hints(fget)
hint = hints.get("return", Any)
hint = kwargs.pop("return_type", None) or get_type_hints(fget).get(
"return", Any
)
kwargs["_js_expr"] = kwargs.pop("_js_expr", fget.__name__)
kwargs["_var_type"] = kwargs.pop("_var_type", hint)
@ -2667,7 +2668,7 @@ def generic_type_to_actual_type_map(
# call recursively for nested generic types and merge the results
return {
k: v
for generic_arg, actual_arg in zip(generic_args, actual_args, strict=False)
for generic_arg, actual_arg in zip(generic_args, actual_args)
for k, v in generic_type_to_actual_type_map(generic_arg, actual_arg).items()
}

View File

@ -21,6 +21,7 @@ from typing import (
from reflex.constants.base import Dirs
from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError
from reflex.utils.imports import ImportDict, ImportVar
from reflex.utils.types import is_optional
from .base import (
CustomVarOperationReturn,
@ -524,6 +525,8 @@ class NumberVar(Var[NUMBER_T]):
Returns:
The boolean value of the number.
"""
if is_optional(self._var_type):
return boolify((self != None) & (self != 0)) # noqa: E711
return self != 0
def _is_strict_float(self) -> bool:

View File

@ -194,14 +194,6 @@ class StringVar(Var[str]):
"""
return string_strip_operation(self)
def bool(self):
"""Boolean conversion.
Returns:
The boolean value of the string.
"""
return self.length() != 0
def reversed(self) -> StringVar:
"""Reverse the string.
@ -600,6 +592,29 @@ class LiteralStringVar(LiteralVar, StringVar):
else:
return only_string.to(StringVar, only_string._var_type)
if len(
literal_strings := [
s
for s in filtered_strings_and_vals
if isinstance(s, (str, LiteralStringVar))
]
) == len(filtered_strings_and_vals):
return LiteralStringVar.create(
"".join(
s._var_value if isinstance(s, LiteralStringVar) else s
for s in literal_strings
),
_var_type=_var_type,
_var_data=VarData.merge(
_var_data,
*(
s._get_all_var_data()
for s in filtered_strings_and_vals
if isinstance(s, Var)
),
),
)
concat_result = ConcatVarOperation.create(
*filtered_strings_and_vals,
_var_data=_var_data,
@ -744,6 +759,26 @@ class ArrayVar(Var[ARRAY_VAR_TYPE]):
"""
if not isinstance(sep, (StringVar, str)):
raise_unsupported_operand_types("join", (type(self), type(sep)))
if (
isinstance(self, LiteralArrayVar)
and (
len(
args := [
x
for x in self._var_value
if isinstance(x, (LiteralStringVar, str))
]
)
== len(self._var_value)
)
and isinstance(sep, (LiteralStringVar, str))
):
sep_str = sep._var_value if isinstance(sep, LiteralStringVar) else sep
return LiteralStringVar.create(
sep_str.join(
i._var_value if isinstance(i, LiteralStringVar) else i for i in args
)
)
return array_join_operation(self, sep)
def reverse(self) -> ArrayVar[ARRAY_VAR_TYPE]:

View File

@ -0,0 +1,74 @@
from reflex.components.el.elements.media import (
Circle,
Defs,
Ellipse,
Line,
LinearGradient,
Path,
Polygon,
RadialGradient,
Rect,
Stop,
Svg,
Text,
)
def test_circle():
circle = Circle.create().render()
assert circle["name"] == "circle"
def test_defs():
defs = Defs.create().render()
assert defs["name"] == "defs"
def test_ellipse():
ellipse = Ellipse.create().render()
assert ellipse["name"] == "ellipse"
def test_line():
line = Line.create().render()
assert line["name"] == "line"
def test_linear_gradient():
linear_gradient = LinearGradient.create().render()
assert linear_gradient["name"] == "linearGradient"
def test_path():
path = Path.create().render()
assert path["name"] == "path"
def test_polygon():
polygon = Polygon.create().render()
assert polygon["name"] == "polygon"
def test_radial_gradient():
radial_gradient = RadialGradient.create().render()
assert radial_gradient["name"] == "radialGradient"
def test_rect():
rect = Rect.create().render()
assert rect["name"] == "rect"
def test_svg():
svg = Svg.create().render()
assert svg["name"] == "svg"
def test_text():
text = Text.create().render()
assert text["name"] == "text"
def test_stop():
stop = Stop.create().render()
assert stop["name"] == "stop"

View File

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

View File

@ -13,7 +13,7 @@ function do_export () {
reflex init --template "$template"
reflex export
(
cd "$SCRIPTPATH/../.."
cd "$SCRIPTPATH/../../.."
scripts/integration.sh ~/"$template" dev
pkill -9 -f 'next-server|python3' || true
sleep 10

View File

@ -0,0 +1,167 @@
"""Integration tests for var operations."""
import time
from typing import Callable, Generator, TypeVar
import pytest
from selenium.webdriver.common.by import By
from reflex.testing import AppHarness
# pyright: reportOptionalMemberAccess=false, reportGeneralTypeIssues=false, reportUnknownMemberType=false
def DynamicComponents():
"""App with var operations."""
import reflex as rx
class DynamicComponentsState(rx.State):
value: int = 10
button: rx.Component = rx.button(
"Click me",
custom_attrs={
"id": "button",
},
)
def got_clicked(self):
self.button = rx.button(
"Clicked",
custom_attrs={
"id": "button",
},
)
@rx.var
def client_token_component(self) -> rx.Component:
return rx.vstack(
rx.el.input(
custom_attrs={
"id": "token",
},
value=self.router.session.client_token,
is_read_only=True,
),
rx.button(
"Update",
custom_attrs={
"id": "update",
},
on_click=DynamicComponentsState.got_clicked,
),
)
app = rx.App()
def factorial(n: int) -> int:
if n == 0:
return 1
return n * factorial(n - 1)
@app.add_page
def index():
return rx.vstack(
DynamicComponentsState.client_token_component,
DynamicComponentsState.button,
rx.text(
DynamicComponentsState._evaluate(lambda state: factorial(state.value)),
id="factorial",
),
)
@pytest.fixture(scope="module")
def dynamic_components(tmp_path_factory) -> Generator[AppHarness, None, None]:
"""Start VarOperations app at tmp_path via AppHarness.
Args:
tmp_path_factory: pytest tmp_path_factory fixture
Yields:
running AppHarness instance
"""
with AppHarness.create(
root=tmp_path_factory.mktemp("dynamic_components"),
app_source=DynamicComponents, # type: ignore
) as harness:
assert harness.app_instance is not None, "app is not running"
yield harness
T = TypeVar("T")
def poll_for_result(
f: Callable[[], T], exception=Exception, max_attempts=5, seconds_between_attempts=1
) -> T:
"""Poll for a result from a function.
Args:
f: function to call
exception: exception to catch
max_attempts: maximum number of attempts
seconds_between_attempts: seconds to wait between
Returns:
Result of the function
Raises:
AssertionError: if the function does not return a value
"""
attempts = 0
while attempts < max_attempts:
try:
return f()
except exception:
attempts += 1
time.sleep(seconds_between_attempts)
raise AssertionError("Function did not return a value")
@pytest.fixture
def driver(dynamic_components: AppHarness):
"""Get an instance of the browser open to the dynamic components app.
Args:
dynamic_components: AppHarness for the dynamic components
Yields:
WebDriver instance.
"""
driver = dynamic_components.frontend()
try:
token_input = poll_for_result(lambda: driver.find_element(By.ID, "token"))
assert token_input
# wait for the backend connection to send the token
token = dynamic_components.poll_for_value(token_input)
assert token is not None
yield driver
finally:
driver.quit()
def test_dynamic_components(driver, dynamic_components: AppHarness):
"""Test that the var operations produce the right results.
Args:
driver: selenium WebDriver open to the app
dynamic_components: AppHarness for the dynamic components
"""
button = poll_for_result(lambda: driver.find_element(By.ID, "button"))
assert button
assert button.text == "Click me"
update_button = driver.find_element(By.ID, "update")
assert update_button
update_button.click()
assert (
dynamic_components.poll_for_content(button, exp_not_equal="Click me")
== "Clicked"
)
factorial = poll_for_result(lambda: driver.find_element(By.ID, "factorial"))
assert factorial
assert factorial.text == "3628800"

Some files were not shown because too many files have changed in this diff Show More