Merge branch 'main' into lendemor/improve_typings
This commit is contained in:
commit
a3869483fa
.github/workflows
benchmarks
poetry.lockpyproject.tomlreflex
.templates/web/components/reflex
app.pyapp_mixins
app_module_for_backend.pycomponents
config.pyconstants
event.pyexperimental
reflex.pystate.pytesting.pyutils
vars
scripts
tests
integration
conftest.pytest_background_task.pytest_call_script.pytest_client_storage.pytest_component_state.pytest_connection_banner.pytest_deploy_url.pytest_dynamic_routes.pytest_event_actions.pytest_event_chain.pytest_exception_handlers.pytest_form_submit.pytest_input.pytest_login_flow.pytest_server_side_event.pytest_upload.pytest_var_operations.py
tests_playwright
units
18
.github/workflows/benchmarks.yml
vendored
18
.github/workflows/benchmarks.yml
vendored
@ -81,18 +81,22 @@ jobs:
|
||||
matrix:
|
||||
# Show OS combos first in GUI
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
python-version: ['3.10.16', '3.11.11', '3.12.8']
|
||||
python-version: ["3.10.16", "3.11.11", "3.12.8"]
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.16'
|
||||
python-version: "3.10.16"
|
||||
- os: windows-latest
|
||||
python-version: "3.11.11"
|
||||
# keep only one python version for MacOS
|
||||
- os: macos-latest
|
||||
python-version: '3.10.16'
|
||||
python-version: "3.10.16"
|
||||
- os: macos-latest
|
||||
python-version: "3.11.11"
|
||||
include:
|
||||
- os: windows-latest
|
||||
python-version: '3.10.11'
|
||||
python-version: "3.10.11"
|
||||
- os: windows-latest
|
||||
python-version: "3.11.9"
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
@ -155,7 +159,11 @@ jobs:
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up python
|
||||
id: setup-python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Poetry
|
||||
uses: snok/install-poetry@v1
|
||||
with:
|
||||
|
@ -55,7 +55,7 @@ jobs:
|
||||
path: reflex-web
|
||||
- name: Install Requirements for reflex-web
|
||||
working-directory: ./reflex-web
|
||||
run: poetry run uv pip install -r requirements.txt
|
||||
run: poetry run uv pip install $(grep -ivE "reflex " requirements.txt)
|
||||
- name: Install additional dependencies for DB access
|
||||
run: poetry run uv pip install psycopg
|
||||
- name: Init Website for reflex-web
|
||||
@ -73,7 +73,7 @@ jobs:
|
||||
echo "$outdated"
|
||||
|
||||
# Ignore 3rd party dependencies that are not updated.
|
||||
filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw|remark-unwrap-images' || true)
|
||||
filtered_outdated=$(echo "$outdated" | grep -vE 'Package|@chakra-ui|lucide-react|@splinetool/runtime|ag-grid-react|framer-motion|react-markdown|remark-math|remark-gfm|rehype-katex|rehype-raw|remark-unwrap-images|ag-grid' || true)
|
||||
no_extra=$(echo "$filtered_outdated" | grep -vE '\|\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-' || true)
|
||||
|
||||
|
||||
|
@ -50,14 +50,7 @@ jobs:
|
||||
- run: poetry run uv pip install pyvirtualdisplay pillow pytest-split pytest-retry
|
||||
- name: Run app harness tests
|
||||
env:
|
||||
SCREENSHOT_DIR: /tmp/screenshots/${{ matrix.state_manager }}/${{ matrix.python-version }}/${{ matrix.split_index }}
|
||||
REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }}
|
||||
run: |
|
||||
poetry run playwright install chromium
|
||||
poetry run pytest tests/integration --retries 3 --maxfail=5 --splits 2 --group ${{matrix.split_index}}
|
||||
- uses: actions/upload-artifact@v4
|
||||
name: Upload failed test screenshots
|
||||
if: always()
|
||||
with:
|
||||
name: failed_test_screenshots
|
||||
path: /tmp/screenshots
|
||||
|
@ -21,7 +21,7 @@ def get_package_size(venv_path: Path, os_name):
|
||||
ValueError: when venv does not exist or python version is None.
|
||||
"""
|
||||
python_version = get_python_version(venv_path, os_name)
|
||||
print("Python version:", python_version) # noqa: T201
|
||||
print("Python version:", python_version)
|
||||
if python_version is None:
|
||||
raise ValueError("Error: Failed to determine Python version.")
|
||||
|
||||
|
@ -122,7 +122,7 @@ def AppWithTenComponentsOnePage():
|
||||
def index() -> rx.Component:
|
||||
return rx.center(rx.vstack(*render_component(1)))
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
@ -133,7 +133,7 @@ def AppWithHundredComponentOnePage():
|
||||
def index() -> rx.Component:
|
||||
return rx.center(rx.vstack(*render_component(100)))
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ def AppWithThousandComponentsOnePage():
|
||||
def index() -> rx.Component:
|
||||
return rx.center(rx.vstack(*render_component(1000)))
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
|
@ -162,7 +162,7 @@ def AppWithOnePage():
|
||||
height="100vh",
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
@ -170,7 +170,7 @@ def AppWithTenPages():
|
||||
"""A reflex app with 10 pages."""
|
||||
import reflex as rx
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
render_multiple_pages(app, 10)
|
||||
|
||||
|
||||
@ -178,7 +178,7 @@ def AppWithHundredPages():
|
||||
"""A reflex app with 100 pages."""
|
||||
import reflex as rx
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
render_multiple_pages(app, 100)
|
||||
|
||||
|
||||
@ -186,7 +186,7 @@ def AppWithThousandPages():
|
||||
"""A reflex app with Thousand pages."""
|
||||
import reflex as rx
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
render_multiple_pages(app, 1000)
|
||||
|
||||
|
||||
@ -194,7 +194,7 @@ def AppWithTenThousandPages():
|
||||
"""A reflex app with ten thousand pages."""
|
||||
import reflex as rx
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
render_multiple_pages(app, 10000)
|
||||
|
||||
|
||||
|
367
poetry.lock
generated
367
poetry.lock
generated
@ -2,13 +2,13 @@
|
||||
|
||||
[[package]]
|
||||
name = "alembic"
|
||||
version = "1.14.0"
|
||||
version = "1.14.1"
|
||||
description = "A database migration tool for SQLAlchemy."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "alembic-1.14.0-py3-none-any.whl", hash = "sha256:99bd884ca390466db5e27ffccff1d179ec5c05c965cfefc0607e69f9e411cb25"},
|
||||
{file = "alembic-1.14.0.tar.gz", hash = "sha256:b00892b53b3642d0b8dbedba234dbf1924b69be83a9a769d5a624b01094e304b"},
|
||||
{file = "alembic-1.14.1-py3-none-any.whl", hash = "sha256:1acdd7a3a478e208b0503cd73614d5e4c6efafa4e73518bb60e4f2846a37b1c5"},
|
||||
{file = "alembic-1.14.1.tar.gz", hash = "sha256:496e888245a53adf1498fcab31713a469c65836f8de76e01399aa1c3e90dd213"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -17,7 +17,7 @@ SQLAlchemy = ">=1.3.0"
|
||||
typing-extensions = ">=4"
|
||||
|
||||
[package.extras]
|
||||
tz = ["backports.zoneinfo"]
|
||||
tz = ["backports.zoneinfo", "tzdata"]
|
||||
|
||||
[[package]]
|
||||
name = "annotated-types"
|
||||
@ -461,7 +461,6 @@ files = [
|
||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:60eb32934076fa07e4316b7b2742fa52cbb190b42c2df2863dbc4230a0a9b385"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e"},
|
||||
{file = "cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053"},
|
||||
@ -472,7 +471,6 @@ files = [
|
||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:9abcc2e083cbe8dde89124a47e5e53ec38751f0d7dfd36801008f316a127d7ba"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285"},
|
||||
{file = "cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417"},
|
||||
@ -594,18 +592,18 @@ standard = ["email-validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "htt
|
||||
|
||||
[[package]]
|
||||
name = "filelock"
|
||||
version = "3.16.1"
|
||||
version = "3.17.0"
|
||||
description = "A platform independent file lock."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "filelock-3.16.1-py3-none-any.whl", hash = "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0"},
|
||||
{file = "filelock-3.16.1.tar.gz", hash = "sha256:c249fbfcd5db47e5e2d6d62198e565475ee65e4831e2561c8e313fa7eb961435"},
|
||||
{file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"},
|
||||
{file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4.1)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.1)", "diff-cover (>=9.2)", "pytest (>=8.3.3)", "pytest-asyncio (>=0.24)", "pytest-cov (>=5)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.26.4)"]
|
||||
docs = ["furo (>=2024.8.6)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"]
|
||||
testing = ["covdefaults (>=2.3)", "coverage (>=7.6.10)", "diff-cover (>=9.2.1)", "pytest (>=8.3.4)", "pytest-asyncio (>=0.25.2)", "pytest-cov (>=6)", "pytest-mock (>=3.14)", "pytest-timeout (>=2.3.1)", "virtualenv (>=20.28.1)"]
|
||||
typing = ["typing-extensions (>=4.12.2)"]
|
||||
|
||||
[[package]]
|
||||
@ -771,15 +769,34 @@ http2 = ["h2 (>=3,<5)"]
|
||||
socks = ["socksio (==1.*)"]
|
||||
zstd = ["zstandard (>=0.18.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "id"
|
||||
version = "1.5.0"
|
||||
description = "A tool for generating OIDC identities"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "id-1.5.0-py3-none-any.whl", hash = "sha256:f1434e1cef91f2cbb8a4ec64663d5a23b9ed43ef44c4c957d02583d61714c658"},
|
||||
{file = "id-1.5.0.tar.gz", hash = "sha256:292cb8a49eacbbdbce97244f47a97b4c62540169c976552e497fd57df0734c1d"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
requests = "*"
|
||||
|
||||
[package.extras]
|
||||
dev = ["build", "bump (>=1.3.2)", "id[lint,test]"]
|
||||
lint = ["bandit", "interrogate", "mypy", "ruff (<0.8.2)", "types-requests"]
|
||||
test = ["coverage[toml]", "pretend", "pytest", "pytest-cov"]
|
||||
|
||||
[[package]]
|
||||
name = "identify"
|
||||
version = "2.6.5"
|
||||
version = "2.6.6"
|
||||
description = "File identification library for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"},
|
||||
{file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"},
|
||||
{file = "identify-2.6.6-py2.py3-none-any.whl", hash = "sha256:cbd1810bce79f8b671ecb20f53ee0ae8e86ae84b557de31d89709dc2a48ba881"},
|
||||
{file = "identify-2.6.6.tar.gz", hash = "sha256:7bec12768ed44ea4761efb47806f0a41f86e7c0a5fdf5950d4648c90eca7e251"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
@ -801,13 +818,13 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2
|
||||
|
||||
[[package]]
|
||||
name = "importlib-metadata"
|
||||
version = "8.5.0"
|
||||
version = "8.6.1"
|
||||
description = "Read metadata from Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"},
|
||||
{file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"},
|
||||
{file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
|
||||
{file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -819,7 +836,7 @@ cover = ["pytest-cov"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"]
|
||||
enabler = ["pytest-enabler (>=2.2)"]
|
||||
perf = ["ipython"]
|
||||
test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
|
||||
test = ["flufl.flake8", "importlib_resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"]
|
||||
type = ["pytest-mypy"]
|
||||
|
||||
[[package]]
|
||||
@ -1097,13 +1114,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "more-itertools"
|
||||
version = "10.5.0"
|
||||
version = "10.6.0"
|
||||
description = "More routines for operating on iterables, beyond itertools"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "more-itertools-10.5.0.tar.gz", hash = "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6"},
|
||||
{file = "more_itertools-10.5.0-py3-none-any.whl", hash = "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef"},
|
||||
{file = "more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b"},
|
||||
{file = "more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1152,66 +1169,66 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "numpy"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
description = "Fundamental package for array computing in Python"
|
||||
optional = false
|
||||
python-versions = ">=3.10"
|
||||
files = [
|
||||
{file = "numpy-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5edb4e4caf751c1518e6a26a83501fda79bff41cc59dac48d70e6d65d4ec4440"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa3017c40d513ccac9621a2364f939d39e550c542eb2a894b4c8da92b38896ab"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:61048b4a49b1c93fe13426e04e04fdf5a03f456616f6e98c7576144677598675"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:7671dc19c7019103ca44e8d94917eba8534c76133523ca8406822efdd19c9308"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4250888bcb96617e00bfa28ac24850a83c9f3a16db471eca2ee1f1714df0f957"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a7746f235c47abc72b102d3bce9977714c2444bdfaea7888d241b4c4bb6a78bf"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:059e6a747ae84fce488c3ee397cee7e5f905fd1bda5fb18c66bc41807ff119b2"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f62aa6ee4eb43b024b0e5a01cf65a0bb078ef8c395e8713c6e8a12a697144528"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-win32.whl", hash = "sha256:48fd472630715e1c1c89bf1feab55c29098cb403cc184b4859f9c86d4fcb6a95"},
|
||||
{file = "numpy-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:b541032178a718c165a49638d28272b771053f628382d5e9d1c93df23ff58dbf"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:40f9e544c1c56ba8f1cf7686a8c9b5bb249e665d40d626a23899ba6d5d9e1484"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f9b57eaa3b0cd8db52049ed0330747b0364e899e8a606a624813452b8203d5f7"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bc8a37ad5b22c08e2dbd27df2b3ef7e5c0864235805b1e718a235bcb200cf1cb"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9036d6365d13b6cbe8f27a0eaf73ddcc070cae584e5ff94bb45e3e9d729feab5"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51faf345324db860b515d3f364eaa93d0e0551a88d6218a7d61286554d190d73"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38efc1e56b73cc9b182fe55e56e63b044dd26a72128fd2fbd502f75555d92591"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:31b89fa67a8042e96715c68e071a1200c4e172f93b0fbe01a14c0ff3ff820fc8"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4c86e2a209199ead7ee0af65e1d9992d1dce7e1f63c4b9a616500f93820658d0"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-win32.whl", hash = "sha256:b34d87e8a3090ea626003f87f9392b3929a7bbf4104a05b6667348b6bd4bf1cd"},
|
||||
{file = "numpy-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:360137f8fb1b753c5cde3ac388597ad680eccbbbb3865ab65efea062c4a1fd16"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:694f9e921a0c8f252980e85bce61ebbd07ed2b7d4fa72d0e4246f2f8aa6642ab"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3683a8d166f2692664262fd4900f207791d005fb088d7fdb973cc8d663626faa"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:780077d95eafc2ccc3ced969db22377b3864e5b9a0ea5eb347cc93b3ea900315"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:55ba24ebe208344aa7a00e4482f65742969a039c2acfcb910bc6fcd776eb4355"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b1d07b53b78bf84a96898c1bc139ad7f10fda7423f5fd158fd0f47ec5e01ac7"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5062dc1a4e32a10dc2b8b13cedd58988261416e811c1dc4dbdea4f57eea61b0d"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:fce4f615f8ca31b2e61aa0eb5865a21e14f5629515c9151850aa936c02a1ee51"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:67d4cda6fa6ffa073b08c8372aa5fa767ceb10c9a0587c707505a6d426f4e046"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-win32.whl", hash = "sha256:32cb94448be47c500d2c7a95f93e2f21a01f1fd05dd2beea1ccd049bb6001cd2"},
|
||||
{file = "numpy-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:ba5511d8f31c033a5fcbda22dd5c813630af98c70b2661f2d2c654ae3cdfcfc8"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f1d09e520217618e76396377c81fba6f290d5f926f50c35f3a5f72b01a0da780"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3ecc47cd7f6ea0336042be87d9e7da378e5c7e9b3c8ad0f7c966f714fc10d821"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f419290bc8968a46c4933158c91a0012b7a99bb2e465d5ef5293879742f8797e"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:5b6c390bfaef8c45a260554888966618328d30e72173697e5cabe6b285fb2348"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:526fc406ab991a340744aad7e25251dd47a6720a685fa3331e5c59fef5282a59"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74e6fdeb9a265624ec3a3918430205dff1df7e95a230779746a6af78bc615af"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:53c09385ff0b72ba79d8715683c1168c12e0b6e84fb0372e97553d1ea91efe51"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f3eac17d9ec51be534685ba877b6ab5edc3ab7ec95c8f163e5d7b39859524716"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-win32.whl", hash = "sha256:9ad014faa93dbb52c80d8f4d3dcf855865c876c9660cb9bd7553843dd03a4b1e"},
|
||||
{file = "numpy-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:164a829b6aacf79ca47ba4814b130c4020b202522a93d7bff2202bfb33b61c60"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4dfda918a13cc4f81e9118dea249e192ab167a0bb1966272d5503e39234d694e"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:733585f9f4b62e9b3528dd1070ec4f52b8acf64215b60a845fa13ebd73cd0712"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:89b16a18e7bba224ce5114db863e7029803c179979e1af6ad6a6b11f70545008"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:676f4eebf6b2d430300f1f4f4c2461685f8269f94c89698d832cdf9277f30b84"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f5cdf9f493b35f7e41e8368e7d7b4bbafaf9660cba53fb21d2cd174ec09631"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1ad395cf254c4fbb5b2132fee391f361a6e8c1adbd28f2cd8e79308a615fe9d"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:08ef779aed40dbc52729d6ffe7dd51df85796a702afbf68a4f4e41fafdc8bda5"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:26c9c4382b19fcfbbed3238a14abf7ff223890ea1936b8890f058e7ba35e8d71"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-win32.whl", hash = "sha256:93cf4e045bae74c90ca833cba583c14b62cb4ba2cba0abd2b141ab52548247e2"},
|
||||
{file = "numpy-2.2.1-cp313-cp313t-win_amd64.whl", hash = "sha256:bff7d8ec20f5f42607599f9994770fa65d76edca264a87b5e4ea5629bce12268"},
|
||||
{file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7ba9cc93a91d86365a5d270dee221fdc04fb68d7478e6bf6af650de78a8339e3"},
|
||||
{file = "numpy-2.2.1-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:3d03883435a19794e41f147612a77a8f56d4e52822337844fff3d4040a142964"},
|
||||
{file = "numpy-2.2.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4511d9e6071452b944207c8ce46ad2f897307910b402ea5fa975da32e0102800"},
|
||||
{file = "numpy-2.2.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5c5cc0cbabe9452038ed984d05ac87910f89370b9242371bd9079cb4af61811e"},
|
||||
{file = "numpy-2.2.1.tar.gz", hash = "sha256:45681fd7128c8ad1c379f0ca0776a8b0c6583d2f69889ddac01559dfe4390918"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7079129b64cb78bdc8d611d1fd7e8002c0a2565da6a47c4df8062349fee90e3e"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ec6c689c61df613b783aeb21f945c4cbe6c51c28cb70aae8430577ab39f163e"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:40c7ff5da22cd391944a28c6a9c638a5eef77fcf71d6e3a79e1d9d9e82752715"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:995f9e8181723852ca458e22de5d9b7d3ba4da3f11cc1cb113f093b271d7965a"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b78ea78450fd96a498f50ee096f69c75379af5138f7881a51355ab0e11286c97"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fbe72d347fbc59f94124125e73fc4976a06927ebc503ec5afbfb35f193cd957"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8e6da5cffbbe571f93588f562ed130ea63ee206d12851b60819512dd3e1ba50d"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:09d6a2032faf25e8d0cadde7fd6145118ac55d2740132c1d845f98721b5ebcfd"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-win32.whl", hash = "sha256:159ff6ee4c4a36a23fe01b7c3d07bd8c14cc433d9720f977fcd52c13c0098160"},
|
||||
{file = "numpy-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:64bd6e1762cd7f0986a740fee4dff927b9ec2c5e4d9a28d056eb17d332158014"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:642199e98af1bd2b6aeb8ecf726972d238c9877b0f6e8221ee5ab945ec8a2189"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6d9fc9d812c81e6168b6d405bf00b8d6739a7f72ef22a9214c4241e0dc70b323"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:c7d1fd447e33ee20c1f33f2c8e6634211124a9aabde3c617687d8b739aa69eac"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:451e854cfae0febe723077bd0cf0a4302a5d84ff25f0bfece8f29206c7bed02e"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd249bc894af67cbd8bad2c22e7cbcd46cf87ddfca1f1289d1e7e54868cc785c"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02935e2c3c0c6cbe9c7955a8efa8908dd4221d7755644c59d1bba28b94fd334f"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a972cec723e0563aa0823ee2ab1df0cb196ed0778f173b381c871a03719d4826"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d6d6a0910c3b4368d89dde073e630882cdb266755565155bc33520283b2d9df8"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-win32.whl", hash = "sha256:860fd59990c37c3ef913c3ae390b3929d005243acca1a86facb0773e2d8d9e50"},
|
||||
{file = "numpy-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:da1eeb460ecce8d5b8608826595c777728cdf28ce7b5a5a8c8ac8d949beadcf2"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ac9bea18d6d58a995fac1b2cb4488e17eceeac413af014b1dd26170b766d8467"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23ae9f0c2d889b7b2d88a3791f6c09e2ef827c2446f1c4a3e3e76328ee4afd9a"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:3074634ea4d6df66be04f6728ee1d173cfded75d002c75fac79503a880bf3825"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8ec0636d3f7d68520afc6ac2dc4b8341ddb725039de042faf0e311599f54eb37"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ffbb1acd69fdf8e89dd60ef6182ca90a743620957afb7066385a7bbe88dc748"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0349b025e15ea9d05c3d63f9657707a4e1d471128a3b1d876c095f328f8ff7f0"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:463247edcee4a5537841d5350bc87fe8e92d7dd0e8c71c995d2c6eecb8208278"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dd47ff0cb2a656ad69c38da850df3454da88ee9a6fde0ba79acceee0e79daba"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-win32.whl", hash = "sha256:4525b88c11906d5ab1b0ec1f290996c0020dd318af8b49acaa46f198b1ffc283"},
|
||||
{file = "numpy-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:5acea83b801e98541619af398cc0109ff48016955cc0818f478ee9ef1c5c3dcb"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:b208cfd4f5fe34e1535c08983a1a6803fdbc7a1e86cf13dd0c61de0b51a0aadc"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d0bbe7dd86dca64854f4b6ce2ea5c60b51e36dfd597300057cf473d3615f2369"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:22ea3bb552ade325530e72a0c557cdf2dea8914d3a5e1fecf58fa5dbcc6f43cd"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:128c41c085cab8a85dc29e66ed88c05613dccf6bc28b3866cd16050a2f5448be"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:250c16b277e3b809ac20d1f590716597481061b514223c7badb7a0f9993c7f84"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0c8854b09bc4de7b041148d8550d3bd712b5c21ff6a8ed308085f190235d7ff"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b6fb9c32a91ec32a689ec6410def76443e3c750e7cfc3fb2206b985ffb2b85f0"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:57b4012e04cc12b78590a334907e01b3a85efb2107df2b8733ff1ed05fce71de"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-win32.whl", hash = "sha256:4dbd80e453bd34bd003b16bd802fac70ad76bd463f81f0c518d1245b1c55e3d9"},
|
||||
{file = "numpy-2.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:5a8c863ceacae696aff37d1fd636121f1a512117652e5dfb86031c8d84836369"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b3482cb7b3325faa5f6bc179649406058253d91ceda359c104dac0ad320e1391"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:9491100aba630910489c1d0158034e1c9a6546f0b1340f716d522dc103788e39"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:41184c416143defa34cc8eb9d070b0a5ba4f13a0fa96a709e20584638254b317"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:7dca87ca328f5ea7dafc907c5ec100d187911f94825f8700caac0b3f4c384b49"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bc61b307655d1a7f9f4b043628b9f2b721e80839914ede634e3d485913e1fb2"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fad446ad0bc886855ddf5909cbf8cb5d0faa637aaa6277fb4b19ade134ab3c7"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:149d1113ac15005652e8d0d3f6fd599360e1a708a4f98e43c9c77834a28238cb"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:106397dbbb1896f99e044efc90360d098b3335060375c26aa89c0d8a97c5f648"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-win32.whl", hash = "sha256:0eec19f8af947a61e968d5429f0bd92fec46d92b0008d0a6685b40d6adf8a4f4"},
|
||||
{file = "numpy-2.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:97b974d3ba0fb4612b77ed35d7627490e8e3dff56ab41454d9e8b23448940576"},
|
||||
{file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b0531f0b0e07643eb089df4c509d30d72c9ef40defa53e41363eca8a8cc61495"},
|
||||
{file = "numpy-2.2.2-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e9e82dcb3f2ebbc8cb5ce1102d5f1c5ed236bf8a11730fb45ba82e2841ec21df"},
|
||||
{file = "numpy-2.2.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0d4142eb40ca6f94539e4db929410f2a46052a0fe7a2c1c59f6179c39938d2a"},
|
||||
{file = "numpy-2.2.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:356ca982c188acbfa6af0d694284d8cf20e95b1c3d0aefa8929376fea9146f60"},
|
||||
{file = "numpy-2.2.2.tar.gz", hash = "sha256:ed6906f61834d687738d25988ae117683705636936cc605be0bb208b23df4d8f"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1442,20 +1459,6 @@ pip = ">=23.1.2"
|
||||
graphviz = ["graphviz (>=0.20.1)"]
|
||||
test = ["covdefaults (>=2.3)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "virtualenv (>=20.25,<21)"]
|
||||
|
||||
[[package]]
|
||||
name = "pkginfo"
|
||||
version = "1.12.0"
|
||||
description = "Query metadata from sdists / bdists / installed packages."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pkginfo-1.12.0-py3-none-any.whl", hash = "sha256:dcd589c9be4da8973eceffa247733c144812759aa67eaf4bbf97016a02f39088"},
|
||||
{file = "pkginfo-1.12.0.tar.gz", hash = "sha256:8ad91a0445a036782b9366ef8b8c2c50291f83a553478ba8580c73d3215700cf"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
testing = ["pytest", "pytest-cov", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "platformdirs"
|
||||
version = "4.3.6"
|
||||
@ -1524,13 +1527,13 @@ testing = ["pytest", "pytest-benchmark"]
|
||||
|
||||
[[package]]
|
||||
name = "pre-commit"
|
||||
version = "4.0.1"
|
||||
version = "4.1.0"
|
||||
description = "A framework for managing and maintaining multi-language pre-commit hooks."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "pre_commit-4.0.1-py2.py3-none-any.whl", hash = "sha256:efde913840816312445dc98787724647c65473daefe420785f885e8ed9a06878"},
|
||||
{file = "pre_commit-4.0.1.tar.gz", hash = "sha256:80905ac375958c0444c65e9cebebd948b3cdb518f335a091a670a89d652139d2"},
|
||||
{file = "pre_commit-4.1.0-py2.py3-none-any.whl", hash = "sha256:d29e7cb346295bcc1cc75fc3e92e343495e3ea0196c9ec6ba53f49f10ab6ae7b"},
|
||||
{file = "pre_commit-4.1.0.tar.gz", hash = "sha256:ae3f018575a588e30dfddfab9a05448bfbd6b73d78709617b5a2b853549716d4"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2264,13 +2267,13 @@ jeepney = ">=0.6"
|
||||
|
||||
[[package]]
|
||||
name = "selenium"
|
||||
version = "4.27.1"
|
||||
version = "4.28.0"
|
||||
description = "Official Python bindings for Selenium WebDriver"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "selenium-4.27.1-py3-none-any.whl", hash = "sha256:b89b1f62b5cfe8025868556fe82360d6b649d464f75d2655cb966c8f8447ea18"},
|
||||
{file = "selenium-4.27.1.tar.gz", hash = "sha256:5296c425a75ff1b44d0d5199042b36a6d1ef76c04fb775b97b40be739a9caae2"},
|
||||
{file = "selenium-4.28.0-py3-none-any.whl", hash = "sha256:3d6a2e8e1b850a1078884ea19f4e011ecdc12263434d87a0b78769836fb82dd8"},
|
||||
{file = "selenium-4.28.0.tar.gz", hash = "sha256:a9fae6eef48d470a1b0c6e45185d96f0dafb025e8da4b346cc41e4da3ac54fa0"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2654,19 +2657,19 @@ wsproto = ">=0.14"
|
||||
|
||||
[[package]]
|
||||
name = "twine"
|
||||
version = "6.0.1"
|
||||
version = "6.1.0"
|
||||
description = "Collection of utilities for publishing packages on PyPI"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "twine-6.0.1-py3-none-any.whl", hash = "sha256:9c6025b203b51521d53e200f4a08b116dee7500a38591668c6a6033117bdc218"},
|
||||
{file = "twine-6.0.1.tar.gz", hash = "sha256:36158b09df5406e1c9c1fb8edb24fc2be387709443e7376689b938531582ee27"},
|
||||
{file = "twine-6.1.0-py3-none-any.whl", hash = "sha256:a47f973caf122930bf0fbbf17f80b83bc1602c9ce393c7845f289a3001dc5384"},
|
||||
{file = "twine-6.1.0.tar.gz", hash = "sha256:be324f6272eff91d07ee93f251edf232fc647935dd585ac003539b42404a8dbd"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
id = "*"
|
||||
keyring = {version = ">=15.1", markers = "platform_machine != \"ppc64le\" and platform_machine != \"s390x\""}
|
||||
packaging = "*"
|
||||
pkginfo = ">=1.8.1"
|
||||
packaging = ">=24.0"
|
||||
readme-renderer = ">=35.0"
|
||||
requests = ">=2.20"
|
||||
requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0"
|
||||
@ -2707,13 +2710,13 @@ files = [
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2024.2"
|
||||
version = "2025.1"
|
||||
description = "Provider of IANA time zone data"
|
||||
optional = false
|
||||
python-versions = ">=2"
|
||||
files = [
|
||||
{file = "tzdata-2024.2-py2.py3-none-any.whl", hash = "sha256:a48093786cdcde33cad18c2555e8532f34422074448fbc874186f0abd79565cd"},
|
||||
{file = "tzdata-2024.2.tar.gz", hash = "sha256:7d85cc416e9382e69095b7bdf4afd9e3880418a2413feec7069d533d6b4e31cc"},
|
||||
{file = "tzdata-2025.1-py2.py3-none-any.whl", hash = "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639"},
|
||||
{file = "tzdata-2025.1.tar.gz", hash = "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2757,13 +2760,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.6.3)", "python-dotenv (>=0.13)",
|
||||
|
||||
[[package]]
|
||||
name = "virtualenv"
|
||||
version = "20.28.1"
|
||||
version = "20.29.1"
|
||||
description = "Virtual Python Environment builder"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"},
|
||||
{file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"},
|
||||
{file = "virtualenv-20.29.1-py3-none-any.whl", hash = "sha256:4e4cb403c0b0da39e13b46b1b2476e505cb0046b25f242bee80f62bf990b2779"},
|
||||
{file = "virtualenv-20.29.1.tar.gz", hash = "sha256:b8b8970138d32fb606192cb97f6cd4bb644fa486be9308fb9b63f81091b5dc35"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@ -2793,80 +2796,80 @@ test = ["websockets"]
|
||||
|
||||
[[package]]
|
||||
name = "websockets"
|
||||
version = "14.1"
|
||||
version = "14.2"
|
||||
description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)"
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
files = [
|
||||
{file = "websockets-14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a0adf84bc2e7c86e8a202537b4fd50e6f7f0e4a6b6bf64d7ccb96c4cd3330b29"},
|
||||
{file = "websockets-14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90b5d9dfbb6d07a84ed3e696012610b6da074d97453bd01e0e30744b472c8179"},
|
||||
{file = "websockets-14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2177ee3901075167f01c5e335a6685e71b162a54a89a56001f1c3e9e3d2ad250"},
|
||||
{file = "websockets-14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f14a96a0034a27f9d47fd9788913924c89612225878f8078bb9d55f859272b0"},
|
||||
{file = "websockets-14.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f874ba705deea77bcf64a9da42c1f5fc2466d8f14daf410bc7d4ceae0a9fcb0"},
|
||||
{file = "websockets-14.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9607b9a442392e690a57909c362811184ea429585a71061cd5d3c2b98065c199"},
|
||||
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:bea45f19b7ca000380fbd4e02552be86343080120d074b87f25593ce1700ad58"},
|
||||
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:219c8187b3ceeadbf2afcf0f25a4918d02da7b944d703b97d12fb01510869078"},
|
||||
{file = "websockets-14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ad2ab2547761d79926effe63de21479dfaf29834c50f98c4bf5b5480b5838434"},
|
||||
{file = "websockets-14.1-cp310-cp310-win32.whl", hash = "sha256:1288369a6a84e81b90da5dbed48610cd7e5d60af62df9851ed1d1d23a9069f10"},
|
||||
{file = "websockets-14.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0744623852f1497d825a49a99bfbec9bea4f3f946df6eb9d8a2f0c37a2fec2e"},
|
||||
{file = "websockets-14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:449d77d636f8d9c17952628cc7e3b8faf6e92a17ec581ec0c0256300717e1512"},
|
||||
{file = "websockets-14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a35f704be14768cea9790d921c2c1cc4fc52700410b1c10948511039be824aac"},
|
||||
{file = "websockets-14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b1f3628a0510bd58968c0f60447e7a692933589b791a6b572fcef374053ca280"},
|
||||
{file = "websockets-14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c3deac3748ec73ef24fc7be0b68220d14d47d6647d2f85b2771cb35ea847aa1"},
|
||||
{file = "websockets-14.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7048eb4415d46368ef29d32133134c513f507fff7d953c18c91104738a68c3b3"},
|
||||
{file = "websockets-14.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6cf0ad281c979306a6a34242b371e90e891bce504509fb6bb5246bbbf31e7b6"},
|
||||
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cc1fc87428c1d18b643479caa7b15db7d544652e5bf610513d4a3478dbe823d0"},
|
||||
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f95ba34d71e2fa0c5d225bde3b3bdb152e957150100e75c86bc7f3964c450d89"},
|
||||
{file = "websockets-14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9481a6de29105d73cf4515f2bef8eb71e17ac184c19d0b9918a3701c6c9c4f23"},
|
||||
{file = "websockets-14.1-cp311-cp311-win32.whl", hash = "sha256:368a05465f49c5949e27afd6fbe0a77ce53082185bbb2ac096a3a8afaf4de52e"},
|
||||
{file = "websockets-14.1-cp311-cp311-win_amd64.whl", hash = "sha256:6d24fc337fc055c9e83414c94e1ee0dee902a486d19d2a7f0929e49d7d604b09"},
|
||||
{file = "websockets-14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ed907449fe5e021933e46a3e65d651f641975a768d0649fee59f10c2985529ed"},
|
||||
{file = "websockets-14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:87e31011b5c14a33b29f17eb48932e63e1dcd3fa31d72209848652310d3d1f0d"},
|
||||
{file = "websockets-14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:bc6ccf7d54c02ae47a48ddf9414c54d48af9c01076a2e1023e3b486b6e72c707"},
|
||||
{file = "websockets-14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9777564c0a72a1d457f0848977a1cbe15cfa75fa2f67ce267441e465717dcf1a"},
|
||||
{file = "websockets-14.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a655bde548ca98f55b43711b0ceefd2a88a71af6350b0c168aa77562104f3f45"},
|
||||
{file = "websockets-14.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a3dfff83ca578cada2d19e665e9c8368e1598d4e787422a460ec70e531dbdd58"},
|
||||
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6a6c9bcf7cdc0fd41cc7b7944447982e8acfd9f0d560ea6d6845428ed0562058"},
|
||||
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:4b6caec8576e760f2c7dd878ba817653144d5f369200b6ddf9771d64385b84d4"},
|
||||
{file = "websockets-14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:eb6d38971c800ff02e4a6afd791bbe3b923a9a57ca9aeab7314c21c84bf9ff05"},
|
||||
{file = "websockets-14.1-cp312-cp312-win32.whl", hash = "sha256:1d045cbe1358d76b24d5e20e7b1878efe578d9897a25c24e6006eef788c0fdf0"},
|
||||
{file = "websockets-14.1-cp312-cp312-win_amd64.whl", hash = "sha256:90f4c7a069c733d95c308380aae314f2cb45bd8a904fb03eb36d1a4983a4993f"},
|
||||
{file = "websockets-14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:3630b670d5057cd9e08b9c4dab6493670e8e762a24c2c94ef312783870736ab9"},
|
||||
{file = "websockets-14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36ebd71db3b89e1f7b1a5deaa341a654852c3518ea7a8ddfdf69cc66acc2db1b"},
|
||||
{file = "websockets-14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5b918d288958dc3fa1c5a0b9aa3256cb2b2b84c54407f4813c45d52267600cd3"},
|
||||
{file = "websockets-14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:00fe5da3f037041da1ee0cf8e308374e236883f9842c7c465aa65098b1c9af59"},
|
||||
{file = "websockets-14.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8149a0f5a72ca36720981418eeffeb5c2729ea55fa179091c81a0910a114a5d2"},
|
||||
{file = "websockets-14.1-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77569d19a13015e840b81550922056acabc25e3f52782625bc6843cfa034e1da"},
|
||||
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cf5201a04550136ef870aa60ad3d29d2a59e452a7f96b94193bee6d73b8ad9a9"},
|
||||
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:88cf9163ef674b5be5736a584c999e98daf3aabac6e536e43286eb74c126b9c7"},
|
||||
{file = "websockets-14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:836bef7ae338a072e9d1863502026f01b14027250a4545672673057997d5c05a"},
|
||||
{file = "websockets-14.1-cp313-cp313-win32.whl", hash = "sha256:0d4290d559d68288da9f444089fd82490c8d2744309113fc26e2da6e48b65da6"},
|
||||
{file = "websockets-14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8621a07991add373c3c5c2cf89e1d277e49dc82ed72c75e3afc74bd0acc446f0"},
|
||||
{file = "websockets-14.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:01bb2d4f0a6d04538d3c5dfd27c0643269656c28045a53439cbf1c004f90897a"},
|
||||
{file = "websockets-14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:414ffe86f4d6f434a8c3b7913655a1a5383b617f9bf38720e7c0799fac3ab1c6"},
|
||||
{file = "websockets-14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8fda642151d5affdee8a430bd85496f2e2517be3a2b9d2484d633d5712b15c56"},
|
||||
{file = "websockets-14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd7c11968bc3860d5c78577f0dbc535257ccec41750675d58d8dc66aa47fe52c"},
|
||||
{file = "websockets-14.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a032855dc7db987dff813583d04f4950d14326665d7e714d584560b140ae6b8b"},
|
||||
{file = "websockets-14.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7e7ea2f782408c32d86b87a0d2c1fd8871b0399dd762364c731d86c86069a78"},
|
||||
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:39450e6215f7d9f6f7bc2a6da21d79374729f5d052333da4d5825af8a97e6735"},
|
||||
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ceada5be22fa5a5a4cdeec74e761c2ee7db287208f54c718f2df4b7e200b8d4a"},
|
||||
{file = "websockets-14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3fc753451d471cff90b8f467a1fc0ae64031cf2d81b7b34e1811b7e2691bc4bc"},
|
||||
{file = "websockets-14.1-cp39-cp39-win32.whl", hash = "sha256:14839f54786987ccd9d03ed7f334baec0f02272e7ec4f6e9d427ff584aeea8b4"},
|
||||
{file = "websockets-14.1-cp39-cp39-win_amd64.whl", hash = "sha256:d9fd19ecc3a4d5ae82ddbfb30962cf6d874ff943e56e0c81f5169be2fda62979"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5dc25a9dbd1a7f61eca4b7cb04e74ae4b963d658f9e4f9aad9cd00b688692c8"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:04a97aca96ca2acedf0d1f332c861c5a4486fdcba7bcef35873820f940c4231e"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df174ece723b228d3e8734a6f2a6febbd413ddec39b3dc592f5a4aa0aff28098"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:034feb9f4286476f273b9a245fb15f02c34d9586a5bc936aff108c3ba1b21beb"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c308dabd2b380807ab64b62985eaccf923a78ebc572bd485375b9ca2b7dc7"},
|
||||
{file = "websockets-14.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:5a42d3ecbb2db5080fc578314439b1d79eef71d323dc661aa616fb492436af5d"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ddaa4a390af911da6f680be8be4ff5aaf31c4c834c1a9147bc21cbcbca2d4370"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a4c805c6034206143fbabd2d259ec5e757f8b29d0a2f0bf3d2fe5d1f60147a4a"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:205f672a6c2c671a86d33f6d47c9b35781a998728d2c7c2a3e1cf3333fcb62b7"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef440054124728cc49b01c33469de06755e5a7a4e83ef61934ad95fc327fbb0"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7591d6f440af7f73c4bd9404f3772bfee064e639d2b6cc8c94076e71b2471c1"},
|
||||
{file = "websockets-14.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:25225cc79cfebc95ba1d24cd3ab86aaa35bcd315d12fa4358939bd55e9bd74a5"},
|
||||
{file = "websockets-14.1-py3-none-any.whl", hash = "sha256:4d4fc827a20abe6d544a119896f6b78ee13fe81cbfef416f3f2ddf09a03f0e2e"},
|
||||
{file = "websockets-14.1.tar.gz", hash = "sha256:398b10c77d471c0aab20a845e7a60076b6390bfdaac7a6d2edb0d2c59d75e8d8"},
|
||||
{file = "websockets-14.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e8179f95323b9ab1c11723e5d91a89403903f7b001828161b480a7810b334885"},
|
||||
{file = "websockets-14.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0d8c3e2cdb38f31d8bd7d9d28908005f6fa9def3324edb9bf336d7e4266fd397"},
|
||||
{file = "websockets-14.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:714a9b682deb4339d39ffa674f7b674230227d981a37d5d174a4a83e3978a610"},
|
||||
{file = "websockets-14.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2e53c72052f2596fb792a7acd9704cbc549bf70fcde8a99e899311455974ca3"},
|
||||
{file = "websockets-14.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e3fbd68850c837e57373d95c8fe352203a512b6e49eaae4c2f4088ef8cf21980"},
|
||||
{file = "websockets-14.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b27ece32f63150c268593d5fdb82819584831a83a3f5809b7521df0685cd5d8"},
|
||||
{file = "websockets-14.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4daa0faea5424d8713142b33825fff03c736f781690d90652d2c8b053345b0e7"},
|
||||
{file = "websockets-14.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:bc63cee8596a6ec84d9753fd0fcfa0452ee12f317afe4beae6b157f0070c6c7f"},
|
||||
{file = "websockets-14.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a570862c325af2111343cc9b0257b7119b904823c675b22d4ac547163088d0d"},
|
||||
{file = "websockets-14.2-cp310-cp310-win32.whl", hash = "sha256:75862126b3d2d505e895893e3deac0a9339ce750bd27b4ba515f008b5acf832d"},
|
||||
{file = "websockets-14.2-cp310-cp310-win_amd64.whl", hash = "sha256:cc45afb9c9b2dc0852d5c8b5321759cf825f82a31bfaf506b65bf4668c96f8b2"},
|
||||
{file = "websockets-14.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3bdc8c692c866ce5fefcaf07d2b55c91d6922ac397e031ef9b774e5b9ea42166"},
|
||||
{file = "websockets-14.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c93215fac5dadc63e51bcc6dceca72e72267c11def401d6668622b47675b097f"},
|
||||
{file = "websockets-14.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c9b6535c0e2cf8a6bf938064fb754aaceb1e6a4a51a80d884cd5db569886910"},
|
||||
{file = "websockets-14.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a52a6d7cf6938e04e9dceb949d35fbdf58ac14deea26e685ab6368e73744e4c"},
|
||||
{file = "websockets-14.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9f05702e93203a6ff5226e21d9b40c037761b2cfb637187c9802c10f58e40473"},
|
||||
{file = "websockets-14.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22441c81a6748a53bfcb98951d58d1af0661ab47a536af08920d129b4d1c3473"},
|
||||
{file = "websockets-14.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd9b868d78b194790e6236d9cbc46d68aba4b75b22497eb4ab64fa640c3af56"},
|
||||
{file = "websockets-14.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1a5a20d5843886d34ff8c57424cc65a1deda4375729cbca4cb6b3353f3ce4142"},
|
||||
{file = "websockets-14.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:34277a29f5303d54ec6468fb525d99c99938607bc96b8d72d675dee2b9f5bf1d"},
|
||||
{file = "websockets-14.2-cp311-cp311-win32.whl", hash = "sha256:02687db35dbc7d25fd541a602b5f8e451a238ffa033030b172ff86a93cb5dc2a"},
|
||||
{file = "websockets-14.2-cp311-cp311-win_amd64.whl", hash = "sha256:862e9967b46c07d4dcd2532e9e8e3c2825e004ffbf91a5ef9dde519ee2effb0b"},
|
||||
{file = "websockets-14.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1f20522e624d7ffbdbe259c6b6a65d73c895045f76a93719aa10cd93b3de100c"},
|
||||
{file = "websockets-14.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:647b573f7d3ada919fd60e64d533409a79dcf1ea21daeb4542d1d996519ca967"},
|
||||
{file = "websockets-14.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6af99a38e49f66be5a64b1e890208ad026cda49355661549c507152113049990"},
|
||||
{file = "websockets-14.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:091ab63dfc8cea748cc22c1db2814eadb77ccbf82829bac6b2fbe3401d548eda"},
|
||||
{file = "websockets-14.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b374e8953ad477d17e4851cdc66d83fdc2db88d9e73abf755c94510ebddceb95"},
|
||||
{file = "websockets-14.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a39d7eceeea35db85b85e1169011bb4321c32e673920ae9c1b6e0978590012a3"},
|
||||
{file = "websockets-14.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0a6f3efd47ffd0d12080594f434faf1cd2549b31e54870b8470b28cc1d3817d9"},
|
||||
{file = "websockets-14.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:065ce275e7c4ffb42cb738dd6b20726ac26ac9ad0a2a48e33ca632351a737267"},
|
||||
{file = "websockets-14.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e9d0e53530ba7b8b5e389c02282f9d2aa47581514bd6049d3a7cffe1385cf5fe"},
|
||||
{file = "websockets-14.2-cp312-cp312-win32.whl", hash = "sha256:20e6dd0984d7ca3037afcb4494e48c74ffb51e8013cac71cf607fffe11df7205"},
|
||||
{file = "websockets-14.2-cp312-cp312-win_amd64.whl", hash = "sha256:44bba1a956c2c9d268bdcdf234d5e5ff4c9b6dc3e300545cbe99af59dda9dcce"},
|
||||
{file = "websockets-14.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:6f1372e511c7409a542291bce92d6c83320e02c9cf392223272287ce55bc224e"},
|
||||
{file = "websockets-14.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4da98b72009836179bb596a92297b1a61bb5a830c0e483a7d0766d45070a08ad"},
|
||||
{file = "websockets-14.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8a86a269759026d2bde227652b87be79f8a734e582debf64c9d302faa1e9f03"},
|
||||
{file = "websockets-14.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86cf1aaeca909bf6815ea714d5c5736c8d6dd3a13770e885aafe062ecbd04f1f"},
|
||||
{file = "websockets-14.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b0f6c3ba3b1240f602ebb3971d45b02cc12bd1845466dd783496b3b05783a5"},
|
||||
{file = "websockets-14.2-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:669c3e101c246aa85bc8534e495952e2ca208bd87994650b90a23d745902db9a"},
|
||||
{file = "websockets-14.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:eabdb28b972f3729348e632ab08f2a7b616c7e53d5414c12108c29972e655b20"},
|
||||
{file = "websockets-14.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2066dc4cbcc19f32c12a5a0e8cc1b7ac734e5b64ac0a325ff8353451c4b15ef2"},
|
||||
{file = "websockets-14.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ab95d357cd471df61873dadf66dd05dd4709cae001dd6342edafc8dc6382f307"},
|
||||
{file = "websockets-14.2-cp313-cp313-win32.whl", hash = "sha256:a9e72fb63e5f3feacdcf5b4ff53199ec8c18d66e325c34ee4c551ca748623bbc"},
|
||||
{file = "websockets-14.2-cp313-cp313-win_amd64.whl", hash = "sha256:b439ea828c4ba99bb3176dc8d9b933392a2413c0f6b149fdcba48393f573377f"},
|
||||
{file = "websockets-14.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7cd5706caec1686c5d233bc76243ff64b1c0dc445339bd538f30547e787c11fe"},
|
||||
{file = "websockets-14.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ec607328ce95a2f12b595f7ae4c5d71bf502212bddcea528290b35c286932b12"},
|
||||
{file = "websockets-14.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:da85651270c6bfb630136423037dd4975199e5d4114cae6d3066641adcc9d1c7"},
|
||||
{file = "websockets-14.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3ecadc7ce90accf39903815697917643f5b7cfb73c96702318a096c00aa71f5"},
|
||||
{file = "websockets-14.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1979bee04af6a78608024bad6dfcc0cc930ce819f9e10342a29a05b5320355d0"},
|
||||
{file = "websockets-14.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dddacad58e2614a24938a50b85969d56f88e620e3f897b7d80ac0d8a5800258"},
|
||||
{file = "websockets-14.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:89a71173caaf75fa71a09a5f614f450ba3ec84ad9fca47cb2422a860676716f0"},
|
||||
{file = "websockets-14.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:6af6a4b26eea4fc06c6818a6b962a952441e0e39548b44773502761ded8cc1d4"},
|
||||
{file = "websockets-14.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:80c8efa38957f20bba0117b48737993643204645e9ec45512579132508477cfc"},
|
||||
{file = "websockets-14.2-cp39-cp39-win32.whl", hash = "sha256:2e20c5f517e2163d76e2729104abc42639c41cf91f7b1839295be43302713661"},
|
||||
{file = "websockets-14.2-cp39-cp39-win_amd64.whl", hash = "sha256:b4c8cef610e8d7c70dea92e62b6814a8cd24fbd01d7103cc89308d2bfe1659ef"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:d7d9cafbccba46e768be8a8ad4635fa3eae1ffac4c6e7cb4eb276ba41297ed29"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:c76193c1c044bd1e9b3316dcc34b174bbf9664598791e6fb606d8d29000e070c"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd475a974d5352390baf865309fe37dec6831aafc3014ffac1eea99e84e83fc2"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2c6c0097a41968b2e2b54ed3424739aab0b762ca92af2379f152c1aef0187e1c"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d7ff794c8b36bc402f2e07c0b2ceb4a2424147ed4785ff03e2a7af03711d60a"},
|
||||
{file = "websockets-14.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dec254fcabc7bd488dab64846f588fc5b6fe0d78f641180030f8ea27b76d72c3"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bbe03eb853e17fd5b15448328b4ec7fb2407d45fb0245036d06a3af251f8e48f"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a3c4aa3428b904d5404a0ed85f3644d37e2cb25996b7f096d77caeb0e96a3b42"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:577a4cebf1ceaf0b65ffc42c54856214165fb8ceeba3935852fc33f6b0c55e7f"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad1c1d02357b7665e700eca43a31d52814ad9ad9b89b58118bdabc365454b574"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f390024a47d904613577df83ba700bd189eedc09c57af0a904e5c39624621270"},
|
||||
{file = "websockets-14.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c1426c021c38cf92b453cdf371228d3430acd775edee6bac5a4d577efc72365"},
|
||||
{file = "websockets-14.2-py3-none-any.whl", hash = "sha256:7a6ceec4ea84469f15cf15807a747e9efe57e369c384fa86e022b3bea679b79b"},
|
||||
{file = "websockets-14.2.tar.gz", hash = "sha256:5059ed9c54945efb321f097084b4c7e52c246f2c869815876a69d1efc4ad6eb5"},
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -85,16 +85,18 @@ build-backend = "poetry.core.masonry.api"
|
||||
target-version = "py39"
|
||||
output-format = "concise"
|
||||
lint.isort.split-on-trailing-comma = false
|
||||
lint.select = ["ANN001", "B", "C4", "D", "E", "ERA", "F", "FURB", "I", "PERF", "PTH", "RUF", "SIM", "T", "TRY", "W"]
|
||||
lint.select = ["ANN001", "B", "C4", "D", "E", "ERA", "F", "FURB", "I", "N", "PERF", "PTH", "RUF", "SIM", "T", "TRY", "W"]
|
||||
lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF012", "TRY0"]
|
||||
lint.pydocstyle.convention = "google"
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"__init__.py" = ["F401"]
|
||||
"tests/*.py" = ["ANN001","D100", "D103", "D104", "B018", "PERF", "T"]
|
||||
"tests/*.py" = ["ANN001", "D100", "D103", "D104", "B018", "PERF", "T", "N"]
|
||||
"benchmarks/*.py" = ["ANN001", "D100", "D103", "D104", "B018", "PERF", "T", "N"]
|
||||
"reflex/.templates/*.py" = ["D100", "D103", "D104"]
|
||||
"benchmarks/*.py" = ["ANN001"]
|
||||
"*.pyi" = ["D301", "D415", "D417", "D418", "E742"]
|
||||
"*.pyi" = ["D301", "D415", "D417", "D418", "E742", "N"]
|
||||
"pyi_generator.py" = ["N802"]
|
||||
"reflex/constants/*.py" = ["N"]
|
||||
"*/blank.py" = ["I001"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
|
@ -16,10 +16,7 @@ export default function RadixThemesColorModeProvider({ children }) {
|
||||
if (isDevMode) {
|
||||
const lastCompiledTimeInLocalStorage =
|
||||
localStorage.getItem("last_compiled_time");
|
||||
if (
|
||||
lastCompiledTimeInLocalStorage &&
|
||||
lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp
|
||||
) {
|
||||
if (lastCompiledTimeInLocalStorage !== lastCompiledTimeStamp) {
|
||||
// on app startup, make sure the application color mode is persisted correctly.
|
||||
setTheme(defaultColorMode);
|
||||
localStorage.setItem("last_compiled_time", lastCompiledTimeStamp);
|
||||
|
153
reflex/app.py
153
reflex/app.py
@ -251,36 +251,36 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
# Attributes to add to the html root tag of every page.
|
||||
html_custom_attrs: Optional[Dict[str, str]] = None
|
||||
|
||||
# A map from a route to an unevaluated page. PRIVATE.
|
||||
unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
|
||||
# A map from a route to an unevaluated page.
|
||||
_unevaluated_pages: Dict[str, UnevaluatedPage] = dataclasses.field(
|
||||
default_factory=dict
|
||||
)
|
||||
|
||||
# A map from a page route to the component to render. Users should use `add_page`. PRIVATE.
|
||||
pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
|
||||
# A map from a page route to the component to render. Users should use `add_page`.
|
||||
_pages: Dict[str, Component] = dataclasses.field(default_factory=dict)
|
||||
|
||||
# The backend API object. PRIVATE.
|
||||
api: FastAPI = None # type: ignore
|
||||
# The backend API object.
|
||||
_api: FastAPI | None = None
|
||||
|
||||
# The state class to use for the app. PRIVATE.
|
||||
state: Optional[Type[BaseState]] = None
|
||||
# The state class to use for the app.
|
||||
_state: Optional[Type[BaseState]] = None
|
||||
|
||||
# Class to manage many client states.
|
||||
_state_manager: Optional[StateManager] = None
|
||||
|
||||
# Mapping from a route to event handlers to trigger when the page loads. PRIVATE.
|
||||
load_events: Dict[str, List[IndividualEventType[[], Any]]] = dataclasses.field(
|
||||
# Mapping from a route to event handlers to trigger when the page loads.
|
||||
_load_events: Dict[str, List[IndividualEventType[[], Any]]] = dataclasses.field(
|
||||
default_factory=dict
|
||||
)
|
||||
|
||||
# Admin dashboard to view and manage the database. PRIVATE.
|
||||
# Admin dashboard to view and manage the database.
|
||||
admin_dash: Optional[AdminDash] = None
|
||||
|
||||
# The async server name space. PRIVATE.
|
||||
event_namespace: Optional[EventNamespace] = None
|
||||
# The async server name space.
|
||||
_event_namespace: Optional[EventNamespace] = None
|
||||
|
||||
# Background tasks that are currently running. PRIVATE.
|
||||
background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
|
||||
# Background tasks that are currently running.
|
||||
_background_tasks: Set[asyncio.Task] = dataclasses.field(default_factory=set)
|
||||
|
||||
# Frontend Error Handler Function
|
||||
frontend_exception_handler: Callable[[Exception], None] = (
|
||||
@ -292,6 +292,24 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
[Exception], Union[EventSpec, List[EventSpec], None]
|
||||
] = default_backend_exception_handler
|
||||
|
||||
@property
|
||||
def api(self) -> FastAPI | None:
|
||||
"""Get the backend api.
|
||||
|
||||
Returns:
|
||||
The backend api.
|
||||
"""
|
||||
return self._api
|
||||
|
||||
@property
|
||||
def event_namespace(self) -> EventNamespace | None:
|
||||
"""Get the event namespace.
|
||||
|
||||
Returns:
|
||||
The event namespace.
|
||||
"""
|
||||
return self._event_namespace
|
||||
|
||||
def __post_init__(self):
|
||||
"""Initialize the app.
|
||||
|
||||
@ -311,7 +329,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
set_breakpoints(self.style.pop("breakpoints"))
|
||||
|
||||
# Set up the API.
|
||||
self.api = FastAPI(lifespan=self._run_lifespan_tasks)
|
||||
self._api = FastAPI(lifespan=self._run_lifespan_tasks)
|
||||
self._add_cors()
|
||||
self._add_default_endpoints()
|
||||
|
||||
@ -334,8 +352,8 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
def _enable_state(self) -> None:
|
||||
"""Enable state for the app."""
|
||||
if not self.state:
|
||||
self.state = State
|
||||
if not self._state:
|
||||
self._state = State
|
||||
self._setup_state()
|
||||
|
||||
def _setup_state(self) -> None:
|
||||
@ -344,13 +362,13 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
Raises:
|
||||
RuntimeError: If the socket server is invalid.
|
||||
"""
|
||||
if not self.state:
|
||||
if not self._state:
|
||||
return
|
||||
|
||||
config = get_config()
|
||||
|
||||
# Set up the state manager.
|
||||
self._state_manager = StateManager.create(state=self.state)
|
||||
self._state_manager = StateManager.create(state=self._state)
|
||||
|
||||
# Set up the Socket.IO AsyncServer.
|
||||
if not self.sio:
|
||||
@ -381,12 +399,13 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
namespace = config.get_event_namespace()
|
||||
|
||||
# Create the event namespace and attach the main app. Not related to any paths.
|
||||
self.event_namespace = EventNamespace(namespace, self)
|
||||
self._event_namespace = EventNamespace(namespace, self)
|
||||
|
||||
# Register the event namespace with the socket.
|
||||
self.sio.register_namespace(self.event_namespace)
|
||||
# Mount the socket app with the API.
|
||||
self.api.mount(str(constants.Endpoint.EVENT), socket_app)
|
||||
if self.api:
|
||||
self.api.mount(str(constants.Endpoint.EVENT), socket_app)
|
||||
|
||||
# Check the exception handlers
|
||||
self._validate_exception_handlers()
|
||||
@ -397,24 +416,35 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
Returns:
|
||||
The string representation of the app.
|
||||
"""
|
||||
return f"<App state={self.state.__name__ if self.state else None}>"
|
||||
return f"<App state={self._state.__name__ if self._state else None}>"
|
||||
|
||||
def __call__(self) -> FastAPI:
|
||||
"""Run the backend api instance.
|
||||
|
||||
Raises:
|
||||
ValueError: If the app has not been initialized.
|
||||
|
||||
Returns:
|
||||
The backend api.
|
||||
"""
|
||||
if not self.api:
|
||||
raise ValueError("The app has not been initialized.")
|
||||
return self.api
|
||||
|
||||
def _add_default_endpoints(self):
|
||||
"""Add default api endpoints (ping)."""
|
||||
# To test the server.
|
||||
if not self.api:
|
||||
return
|
||||
|
||||
self.api.get(str(constants.Endpoint.PING))(ping)
|
||||
self.api.get(str(constants.Endpoint.HEALTH))(health)
|
||||
|
||||
def _add_optional_endpoints(self):
|
||||
"""Add optional api endpoints (_upload)."""
|
||||
if not self.api:
|
||||
return
|
||||
|
||||
if Upload.is_used:
|
||||
# To upload files.
|
||||
self.api.post(str(constants.Endpoint.UPLOAD))(upload(self))
|
||||
@ -432,6 +462,8 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
def _add_cors(self):
|
||||
"""Add CORS middleware to the app."""
|
||||
if not self.api:
|
||||
return
|
||||
self.api.add_middleware(
|
||||
cors.CORSMiddleware,
|
||||
allow_credentials=True,
|
||||
@ -521,13 +553,13 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
# Check if the route given is valid
|
||||
verify_route_validity(route)
|
||||
|
||||
if route in self.unevaluated_pages and environment.RELOAD_CONFIG.is_set():
|
||||
if route in self._unevaluated_pages and environment.RELOAD_CONFIG.is_set():
|
||||
# when the app is reloaded(typically for app harness tests), we should maintain
|
||||
# the latest render function of a route.This applies typically to decorated pages
|
||||
# since they are only added when app._compile is called.
|
||||
self.unevaluated_pages.pop(route)
|
||||
self._unevaluated_pages.pop(route)
|
||||
|
||||
if route in self.unevaluated_pages:
|
||||
if route in self._unevaluated_pages:
|
||||
route_name = (
|
||||
f"`{route}` or `/`"
|
||||
if route == constants.PageNames.INDEX_ROUTE
|
||||
@ -540,15 +572,15 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
# Setup dynamic args for the route.
|
||||
# this state assignment is only required for tests using the deprecated state kwarg for App
|
||||
state = self.state if self.state else State
|
||||
state = self._state if self._state else State
|
||||
state.setup_dynamic_args(get_route_args(route))
|
||||
|
||||
if on_load:
|
||||
self.load_events[route] = (
|
||||
self._load_events[route] = (
|
||||
on_load if isinstance(on_load, list) else [on_load]
|
||||
)
|
||||
|
||||
self.unevaluated_pages[route] = UnevaluatedPage(
|
||||
self._unevaluated_pages[route] = UnevaluatedPage(
|
||||
component=component,
|
||||
route=route,
|
||||
title=title,
|
||||
@ -563,10 +595,10 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
Args:
|
||||
route: The route of the page to compile.
|
||||
save_page: If True, the compiled page is saved to self.pages.
|
||||
save_page: If True, the compiled page is saved to self._pages.
|
||||
"""
|
||||
component, enable_state = compiler.compile_unevaluated_page(
|
||||
route, self.unevaluated_pages[route], self.state, self.style, self.theme
|
||||
route, self._unevaluated_pages[route], self._state, self.style, self.theme
|
||||
)
|
||||
|
||||
if enable_state:
|
||||
@ -575,7 +607,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
# Add the page.
|
||||
self._check_routes_conflict(route)
|
||||
if save_page:
|
||||
self.pages[route] = component
|
||||
self._pages[route] = component
|
||||
|
||||
def get_load_events(self, route: str) -> list[IndividualEventType[[], Any]]:
|
||||
"""Get the load events for a route.
|
||||
@ -589,7 +621,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
route = route.lstrip("/")
|
||||
if route == "":
|
||||
route = constants.PageNames.INDEX_ROUTE
|
||||
return self.load_events.get(route, [])
|
||||
return self._load_events.get(route, [])
|
||||
|
||||
def _check_routes_conflict(self, new_route: str):
|
||||
"""Verify if there is any conflict between the new route and any existing route.
|
||||
@ -613,7 +645,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
constants.RouteRegex.SINGLE_CATCHALL_SEGMENT,
|
||||
constants.RouteRegex.DOUBLE_CATCHALL_SEGMENT,
|
||||
)
|
||||
for route in self.pages:
|
||||
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("/")
|
||||
@ -671,6 +703,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
def _setup_admin_dash(self):
|
||||
"""Setup the admin dash."""
|
||||
# Get the admin dash.
|
||||
if not self.api:
|
||||
return
|
||||
|
||||
admin_dash = self.admin_dash
|
||||
|
||||
if admin_dash and admin_dash.models:
|
||||
@ -775,10 +810,10 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
def _setup_overlay_component(self):
|
||||
"""If a State is not used and no overlay_component is specified, do not render the connection modal."""
|
||||
if self.state is None and self.overlay_component is default_overlay_component:
|
||||
if self._state is None and self.overlay_component is default_overlay_component:
|
||||
self.overlay_component = None
|
||||
for k, component in self.pages.items():
|
||||
self.pages[k] = self._add_overlay_to_component(component)
|
||||
for k, component in self._pages.items():
|
||||
self._pages[k] = self._add_overlay_to_component(component)
|
||||
|
||||
def _add_error_boundary_to_component(self, component: Component) -> Component:
|
||||
if self.error_boundary is None:
|
||||
@ -790,14 +825,14 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
def _setup_error_boundary(self):
|
||||
"""If a State is not used and no error_boundary is specified, do not render the error boundary."""
|
||||
if self.state is None and self.error_boundary is default_error_boundary:
|
||||
if self._state is None and self.error_boundary is default_error_boundary:
|
||||
self.error_boundary = None
|
||||
|
||||
for k, component in self.pages.items():
|
||||
for k, component in self._pages.items():
|
||||
# Skip the 404 page
|
||||
if k == constants.Page404.SLUG:
|
||||
continue
|
||||
self.pages[k] = self._add_error_boundary_to_component(component)
|
||||
self._pages[k] = self._add_error_boundary_to_component(component)
|
||||
|
||||
def _apply_decorated_pages(self):
|
||||
"""Add @rx.page decorated pages to the app.
|
||||
@ -823,11 +858,11 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
Raises:
|
||||
VarDependencyError: When a computed var has an invalid dependency.
|
||||
"""
|
||||
if not self.state:
|
||||
if not self._state:
|
||||
return
|
||||
|
||||
if not state:
|
||||
state = self.state
|
||||
state = self._state
|
||||
|
||||
for var in state.computed_vars.values():
|
||||
if not var._cache:
|
||||
@ -853,13 +888,13 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
"""
|
||||
from reflex.utils.exceptions import ReflexRuntimeError
|
||||
|
||||
self.pages = {}
|
||||
self._pages = {}
|
||||
|
||||
def get_compilation_time() -> str:
|
||||
return str(datetime.now().time()).split(".")[0]
|
||||
|
||||
# Render a default 404 page if the user didn't supply one
|
||||
if constants.Page404.SLUG not in self.unevaluated_pages:
|
||||
if constants.Page404.SLUG not in self._unevaluated_pages:
|
||||
self.add_page(route=constants.Page404.SLUG)
|
||||
|
||||
# Fix up the style.
|
||||
@ -877,7 +912,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
should_compile = self._should_compile()
|
||||
|
||||
for route in self.unevaluated_pages:
|
||||
for route in self._unevaluated_pages:
|
||||
console.debug(f"Evaluating page: {route}")
|
||||
self._compile_page(route, save_page=should_compile)
|
||||
|
||||
@ -904,7 +939,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
progress.start()
|
||||
task = progress.add_task(
|
||||
f"[{get_compilation_time()}] Compiling:",
|
||||
total=len(self.pages)
|
||||
total=len(self._pages)
|
||||
+ fixed_pages_within_executor
|
||||
+ adhoc_steps_without_executor,
|
||||
)
|
||||
@ -923,7 +958,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
# This has to happen before compiling stateful components as that
|
||||
# prevents recursive functions from reaching all components.
|
||||
for component in self.pages.values():
|
||||
for component in self._pages.values():
|
||||
# Add component._get_all_imports() to all_imports.
|
||||
all_imports.update(component._get_all_imports())
|
||||
|
||||
@ -938,12 +973,12 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
stateful_components_path,
|
||||
stateful_components_code,
|
||||
page_components,
|
||||
) = compiler.compile_stateful_components(self.pages.values())
|
||||
) = compiler.compile_stateful_components(self._pages.values())
|
||||
|
||||
progress.advance(task)
|
||||
|
||||
# Catch "static" apps (that do not define a rx.State subclass) which are trying to access rx.State.
|
||||
if code_uses_state_contexts(stateful_components_code) and self.state is None:
|
||||
if code_uses_state_contexts(stateful_components_code) and self._state is None:
|
||||
raise ReflexRuntimeError(
|
||||
"To access rx.State in frontend components, at least one "
|
||||
"subclass of rx.State must be defined in the app."
|
||||
@ -980,10 +1015,10 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
max_workers=environment.REFLEX_COMPILE_THREADS.get() or None
|
||||
)
|
||||
|
||||
for route, component in zip(self.pages, page_components):
|
||||
for route, component in zip(self._pages, page_components):
|
||||
ExecutorSafeFunctions.COMPONENTS[route] = component
|
||||
|
||||
ExecutorSafeFunctions.STATE = self.state
|
||||
ExecutorSafeFunctions.STATE = self._state
|
||||
|
||||
with executor:
|
||||
result_futures = []
|
||||
@ -993,7 +1028,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
result_futures.append(f)
|
||||
|
||||
# Compile the pre-compiled pages.
|
||||
for route in self.pages:
|
||||
for route in self._pages:
|
||||
_submit_work(
|
||||
ExecutorSafeFunctions.compile_page,
|
||||
route,
|
||||
@ -1028,7 +1063,7 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
|
||||
# Compile the contexts.
|
||||
compile_results.append(
|
||||
compiler.compile_contexts(self.state, self.theme),
|
||||
compiler.compile_contexts(self._state, self.theme),
|
||||
)
|
||||
if self.theme is not None:
|
||||
# Fix #2992 by removing the top-level appearance prop
|
||||
@ -1150,9 +1185,9 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
)
|
||||
|
||||
task = asyncio.create_task(_coro())
|
||||
self.background_tasks.add(task)
|
||||
self._background_tasks.add(task)
|
||||
# Clean up task from background_tasks set when complete.
|
||||
task.add_done_callback(self.background_tasks.discard)
|
||||
task.add_done_callback(self._background_tasks.discard)
|
||||
return task
|
||||
|
||||
def _validate_exception_handlers(self):
|
||||
@ -1162,11 +1197,11 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
ValueError: If the custom exception handlers are invalid.
|
||||
|
||||
"""
|
||||
FRONTEND_ARG_SPEC = {
|
||||
frontend_arg_spec = {
|
||||
"exception": Exception,
|
||||
}
|
||||
|
||||
BACKEND_ARG_SPEC = {
|
||||
backend_arg_spec = {
|
||||
"exception": Exception,
|
||||
}
|
||||
|
||||
@ -1174,8 +1209,8 @@ class App(MiddlewareMixin, LifespanMixin):
|
||||
["frontend", "backend"],
|
||||
[self.frontend_exception_handler, self.backend_exception_handler],
|
||||
[
|
||||
FRONTEND_ARG_SPEC,
|
||||
BACKEND_ARG_SPEC,
|
||||
frontend_arg_spec,
|
||||
backend_arg_spec,
|
||||
],
|
||||
):
|
||||
if hasattr(handler_fn, "__name__"):
|
||||
|
@ -12,7 +12,7 @@ from typing import Callable, Coroutine, Set, Union
|
||||
from fastapi import FastAPI
|
||||
|
||||
from reflex.utils import console
|
||||
from reflex.utils.exceptions import InvalidLifespanTaskType
|
||||
from reflex.utils.exceptions import InvalidLifespanTaskTypeError
|
||||
|
||||
from .mixin import AppMixin
|
||||
|
||||
@ -64,10 +64,10 @@ class LifespanMixin(AppMixin):
|
||||
**task_kwargs: The kwargs of the task.
|
||||
|
||||
Raises:
|
||||
InvalidLifespanTaskType: If the task is a generator function.
|
||||
InvalidLifespanTaskTypeError: If the task is a generator function.
|
||||
"""
|
||||
if inspect.isgeneratorfunction(task) or inspect.isasyncgenfunction(task):
|
||||
raise InvalidLifespanTaskType(
|
||||
raise InvalidLifespanTaskTypeError(
|
||||
f"Task {task.__name__} of type generator must be decorated with contextlib.asynccontextmanager."
|
||||
)
|
||||
|
||||
|
@ -5,14 +5,12 @@ Only the app attribute is explicitly exposed.
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from reflex import constants
|
||||
from reflex.utils import telemetry
|
||||
from reflex.utils.exec import is_prod_mode
|
||||
from reflex.utils.prerequisites import get_and_validate_app
|
||||
|
||||
if constants.CompileVars.APP != "app":
|
||||
raise AssertionError("unexpected variable name for 'app'")
|
||||
|
||||
telemetry.send("compile")
|
||||
app, app_module = get_and_validate_app(reload=False)
|
||||
# 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).
|
||||
@ -31,6 +29,5 @@ del app_module
|
||||
del compile_future
|
||||
del get_and_validate_app
|
||||
del is_prod_mode
|
||||
del telemetry
|
||||
del constants
|
||||
del ThreadPoolExecutor
|
||||
|
@ -14,7 +14,7 @@ class Html(Div):
|
||||
"""
|
||||
|
||||
# The HTML to render.
|
||||
dangerouslySetInnerHTML: Var[Dict[str, str]]
|
||||
dangerouslySetInnerHTML: Var[Dict[str, str]] # noqa: N815
|
||||
|
||||
@classmethod
|
||||
def create(cls, *children, **props):
|
||||
|
@ -192,7 +192,7 @@ class GhostUpload(Fragment):
|
||||
class Upload(MemoizationLeaf):
|
||||
"""A file upload component."""
|
||||
|
||||
library = "react-dropzone@14.2.10"
|
||||
library = "react-dropzone@14.3.5"
|
||||
|
||||
tag = ""
|
||||
|
||||
|
@ -382,7 +382,7 @@ for theme_name in dir(Theme):
|
||||
class CodeBlock(Component, MarkdownComponentMap):
|
||||
"""A code block."""
|
||||
|
||||
library = "react-syntax-highlighter@15.6.0"
|
||||
library = "react-syntax-highlighter@15.6.1"
|
||||
|
||||
tag = "PrismAsyncLight"
|
||||
|
||||
|
@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Union
|
||||
|
||||
from reflex import constants
|
||||
from reflex.utils import imports
|
||||
from reflex.utils.exceptions import DynamicComponentMissingLibrary
|
||||
from reflex.utils.exceptions import DynamicComponentMissingLibraryError
|
||||
from reflex.utils.format import format_library_name
|
||||
from reflex.utils.serializers import serializer
|
||||
from reflex.vars import Var, get_unique_variable_name
|
||||
@ -36,13 +36,15 @@ def bundle_library(component: Union["Component", str]):
|
||||
component: The component to bundle the library with.
|
||||
|
||||
Raises:
|
||||
DynamicComponentMissingLibrary: Raised when a dynamic component is missing a library.
|
||||
DynamicComponentMissingLibraryError: Raised when a dynamic component is missing a library.
|
||||
"""
|
||||
if isinstance(component, str):
|
||||
bundled_libraries.add(component)
|
||||
return
|
||||
if component.library is None:
|
||||
raise DynamicComponentMissingLibrary("Component must have a library to bundle.")
|
||||
raise DynamicComponentMissingLibraryError(
|
||||
"Component must have a library to bundle."
|
||||
)
|
||||
bundled_libraries.add(format_library_name(component.library))
|
||||
|
||||
|
||||
|
@ -5,11 +5,13 @@ from __future__ import annotations
|
||||
from typing import Any, Literal, Optional, Union
|
||||
|
||||
from reflex.event import EventHandler, no_args_event_spec
|
||||
from reflex.utils import types
|
||||
from reflex.utils import console, types
|
||||
from reflex.vars.base import Var
|
||||
|
||||
from .base import NextComponent
|
||||
|
||||
DEFAULT_W_H = "100%"
|
||||
|
||||
|
||||
class Image(NextComponent):
|
||||
"""Display an image."""
|
||||
@ -55,7 +57,7 @@ class Image(NextComponent):
|
||||
loading: Var[Literal["lazy", "eager"]]
|
||||
|
||||
# A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
|
||||
blurDataURL: Var[str]
|
||||
blur_data_url: Var[str]
|
||||
|
||||
# Fires when the image has loaded.
|
||||
on_load: EventHandler[no_args_event_spec]
|
||||
@ -82,8 +84,16 @@ class Image(NextComponent):
|
||||
Returns:
|
||||
_type_: _description_
|
||||
"""
|
||||
if "blurDataURL" in props:
|
||||
console.deprecate(
|
||||
feature_name="blurDataURL",
|
||||
reason="Use blur_data_url instead",
|
||||
deprecation_version="0.7.0",
|
||||
removal_version="0.8.0",
|
||||
)
|
||||
props["blur_data_url"] = props.pop("blurDataURL")
|
||||
|
||||
style = props.get("style", {})
|
||||
DEFAULT_W_H = "100%"
|
||||
|
||||
def check_prop_type(prop_name: str, prop_value: int | str | None):
|
||||
if types.check_prop_in_allowed_types(prop_value, allowed_types=[int]):
|
||||
|
@ -11,6 +11,8 @@ from reflex.vars.base import Var
|
||||
|
||||
from .base import NextComponent
|
||||
|
||||
DEFAULT_W_H = "100%"
|
||||
|
||||
class Image(NextComponent):
|
||||
@overload
|
||||
@classmethod
|
||||
@ -30,7 +32,7 @@ class Image(NextComponent):
|
||||
loading: Optional[
|
||||
Union[Literal["eager", "lazy"], Var[Literal["eager", "lazy"]]]
|
||||
] = None,
|
||||
blurDataURL: Optional[Union[Var[str], str]] = None,
|
||||
blur_data_url: Optional[Union[Var[str], str]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
@ -71,7 +73,7 @@ class Image(NextComponent):
|
||||
priority: When true, the image will be considered high priority and preload. Lazy loading is automatically disabled for images using priority.
|
||||
placeholder: A placeholder to use while the image is loading. Possible values are blur, empty, or data:image/.... Defaults to empty.
|
||||
loading: The loading behavior of the image. Defaults to lazy. Can hurt performance, recommended to use `priority` instead.
|
||||
blurDataURL: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
|
||||
blur_data_url: A Data URL to be used as a placeholder image before the src image successfully loads. Only takes effect when combined with placeholder="blur".
|
||||
on_load: Fires when the image has loaded.
|
||||
on_error: Fires when the image has an error.
|
||||
style: The style of the component.
|
||||
|
@ -95,7 +95,7 @@ class Plotly(NoSSRComponent):
|
||||
|
||||
library = "react-plotly.js@2.6.0"
|
||||
|
||||
lib_dependencies: List[str] = ["plotly.js@2.35.2"]
|
||||
lib_dependencies: List[str] = ["plotly.js@2.35.3"]
|
||||
|
||||
tag = "Plot"
|
||||
|
||||
|
@ -485,11 +485,11 @@ to {
|
||||
Returns:
|
||||
The style of the component.
|
||||
"""
|
||||
slideDown = LiteralVar.create(
|
||||
slide_down = LiteralVar.create(
|
||||
"${slideDown} var(--animation-duration) var(--animation-easing)",
|
||||
)
|
||||
|
||||
slideUp = LiteralVar.create(
|
||||
slide_up = LiteralVar.create(
|
||||
"${slideUp} var(--animation-duration) var(--animation-easing)",
|
||||
)
|
||||
|
||||
@ -503,8 +503,8 @@ to {
|
||||
"display": "block",
|
||||
"height": "var(--space-3)",
|
||||
},
|
||||
"&[data-state='open']": {"animation": slideDown},
|
||||
"&[data-state='closed']": {"animation": slideUp},
|
||||
"&[data-state='open']": {"animation": slide_down},
|
||||
"&[data-state='closed']": {"animation": slide_up},
|
||||
_inherited_variant_selector("classic"): {
|
||||
"color": "var(--accent-contrast)",
|
||||
},
|
||||
|
@ -66,7 +66,7 @@ class DrawerRoot(DrawerComponent):
|
||||
scroll_lock_timeout: Var[int]
|
||||
|
||||
# When `True`, it prevents scroll restoration. Defaults to `True`.
|
||||
preventScrollRestoration: Var[bool]
|
||||
prevent_scroll_restoration: Var[bool]
|
||||
|
||||
# Enable background scaling, it requires container element with `vaul-drawer-wrapper` attribute to scale its background.
|
||||
should_scale_background: Var[bool]
|
||||
|
@ -81,7 +81,7 @@ class DrawerRoot(DrawerComponent):
|
||||
snap_points: Optional[List[Union[float, str]]] = None,
|
||||
fade_from_index: Optional[Union[Var[int], int]] = None,
|
||||
scroll_lock_timeout: Optional[Union[Var[int], int]] = None,
|
||||
preventScrollRestoration: Optional[Union[Var[bool], bool]] = None,
|
||||
prevent_scroll_restoration: Optional[Union[Var[bool], bool]] = None,
|
||||
should_scale_background: Optional[Union[Var[bool], bool]] = None,
|
||||
close_threshold: Optional[Union[Var[float], float]] = None,
|
||||
as_child: Optional[Union[Var[bool], bool]] = None,
|
||||
@ -129,7 +129,7 @@ class DrawerRoot(DrawerComponent):
|
||||
snap_points: Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Also Accept px values, which doesn't take screen height into account.
|
||||
fade_from_index: Index of a snapPoint from which the overlay fade should be applied. Defaults to the last snap point.
|
||||
scroll_lock_timeout: Duration for which the drawer is not draggable after scrolling content inside of the drawer. Defaults to 500ms
|
||||
preventScrollRestoration: When `True`, it prevents scroll restoration. Defaults to `True`.
|
||||
prevent_scroll_restoration: When `True`, it prevents scroll restoration. Defaults to `True`.
|
||||
should_scale_background: Enable background scaling, it requires container element with `vaul-drawer-wrapper` attribute to scale its background.
|
||||
close_threshold: Number between 0 and 1 that determines when the drawer should be closed.
|
||||
as_child: Change the default rendered element for the one passed as a child.
|
||||
@ -567,7 +567,7 @@ class Drawer(ComponentNamespace):
|
||||
snap_points: Optional[List[Union[float, str]]] = None,
|
||||
fade_from_index: Optional[Union[Var[int], int]] = None,
|
||||
scroll_lock_timeout: Optional[Union[Var[int], int]] = None,
|
||||
preventScrollRestoration: Optional[Union[Var[bool], bool]] = None,
|
||||
prevent_scroll_restoration: Optional[Union[Var[bool], bool]] = None,
|
||||
should_scale_background: Optional[Union[Var[bool], bool]] = None,
|
||||
close_threshold: Optional[Union[Var[float], float]] = None,
|
||||
as_child: Optional[Union[Var[bool], bool]] = None,
|
||||
@ -615,7 +615,7 @@ class Drawer(ComponentNamespace):
|
||||
snap_points: Array of numbers from 0 to 100 that corresponds to % of the screen a given snap point should take up. Should go from least visible. Also Accept px values, which doesn't take screen height into account.
|
||||
fade_from_index: Index of a snapPoint from which the overlay fade should be applied. Defaults to the last snap point.
|
||||
scroll_lock_timeout: Duration for which the drawer is not draggable after scrolling content inside of the drawer. Defaults to 500ms
|
||||
preventScrollRestoration: When `True`, it prevents scroll restoration. Defaults to `True`.
|
||||
prevent_scroll_restoration: When `True`, it prevents scroll restoration. Defaults to `True`.
|
||||
should_scale_background: Enable background scaling, it requires container element with `vaul-drawer-wrapper` attribute to scale its background.
|
||||
close_threshold: Number between 0 and 1 that determines when the drawer should be closed.
|
||||
as_child: Change the default rendered element for the one passed as a child.
|
||||
|
@ -22,6 +22,8 @@ from ..base import (
|
||||
|
||||
LiteralButtonSize = Literal["1", "2", "3", "4"]
|
||||
|
||||
RADIX_TO_LUCIDE_SIZE = {"1": 12, "2": 24, "3": 36, "4": 48}
|
||||
|
||||
|
||||
class IconButton(elements.Button, RadixLoadingProp, RadixThemesComponent):
|
||||
"""A button designed specifically for usage with a single icon."""
|
||||
@ -72,8 +74,6 @@ class IconButton(elements.Button, RadixLoadingProp, RadixThemesComponent):
|
||||
"IconButton requires a child icon. Pass a string as the first child or a rx.icon."
|
||||
)
|
||||
if "size" in props:
|
||||
RADIX_TO_LUCIDE_SIZE = {"1": 12, "2": 24, "3": 36, "4": 48}
|
||||
|
||||
if isinstance(props["size"], str):
|
||||
children[0].size = RADIX_TO_LUCIDE_SIZE[props["size"]]
|
||||
else:
|
||||
|
@ -14,6 +14,7 @@ from reflex.vars.base import Var
|
||||
from ..base import RadixLoadingProp, RadixThemesComponent
|
||||
|
||||
LiteralButtonSize = Literal["1", "2", "3", "4"]
|
||||
RADIX_TO_LUCIDE_SIZE = {"1": 12, "2": 24, "3": 36, "4": 48}
|
||||
|
||||
class IconButton(elements.Button, RadixLoadingProp, RadixThemesComponent):
|
||||
@overload
|
||||
|
@ -28,6 +28,9 @@ LiteralStickyType = Literal[
|
||||
]
|
||||
|
||||
|
||||
ARIA_LABEL_KEY = "aria_label"
|
||||
|
||||
|
||||
# The Tooltip inherits props from the Tooltip.Root, Tooltip.Portal, Tooltip.Content
|
||||
class Tooltip(RadixThemesComponent):
|
||||
"""Floating element that provides a control with contextual information via pointer or focus."""
|
||||
@ -104,7 +107,6 @@ class Tooltip(RadixThemesComponent):
|
||||
Returns:
|
||||
The created component.
|
||||
"""
|
||||
ARIA_LABEL_KEY = "aria_label"
|
||||
if props.get(ARIA_LABEL_KEY) is not None:
|
||||
props[format.to_kebab_case(ARIA_LABEL_KEY)] = props.pop(ARIA_LABEL_KEY)
|
||||
|
||||
|
@ -14,6 +14,7 @@ from ..base import RadixThemesComponent
|
||||
LiteralSideType = Literal["top", "right", "bottom", "left"]
|
||||
LiteralAlignType = Literal["start", "center", "end"]
|
||||
LiteralStickyType = Literal["partial", "always"]
|
||||
ARIA_LABEL_KEY = "aria_label"
|
||||
|
||||
class Tooltip(RadixThemesComponent):
|
||||
@overload
|
||||
|
@ -250,10 +250,10 @@ class Cell(Recharts):
|
||||
alias = "RechartsCell"
|
||||
|
||||
# The presentation attribute of a rectangle in bar or a sector in pie.
|
||||
fill: Var[str]
|
||||
fill: Var[str | Color]
|
||||
|
||||
# The presentation attribute of a rectangle in bar or a sector in pie.
|
||||
stroke: Var[str]
|
||||
stroke: Var[str | Color]
|
||||
|
||||
|
||||
responsive_container = ResponsiveContainer.create
|
||||
|
@ -488,8 +488,8 @@ class Cell(Recharts):
|
||||
def create( # type: ignore
|
||||
cls,
|
||||
*children,
|
||||
fill: Optional[Union[Var[str], str]] = None,
|
||||
stroke: Optional[Union[Var[str], str]] = None,
|
||||
fill: Optional[Union[Color, Var[Union[Color, str]], str]] = None,
|
||||
stroke: Optional[Union[Color, Var[Union[Color, str]], str]] = None,
|
||||
style: Optional[Style] = None,
|
||||
key: Optional[Any] = None,
|
||||
id: Optional[Any] = None,
|
||||
|
@ -73,7 +73,7 @@ class Pie(Recharts):
|
||||
data: Var[List[Dict[str, Any]]]
|
||||
|
||||
# Valid children components
|
||||
_valid_children: List[str] = ["Cell", "LabelList"]
|
||||
_valid_children: List[str] = ["Cell", "LabelList", "Bare"]
|
||||
|
||||
# Stoke color. Default: rx.color("accent", 9)
|
||||
stroke: Var[Union[str, Color]] = LiteralVar.create(Color("accent", 9))
|
||||
|
@ -1,6 +1,6 @@
|
||||
"""A component that wraps a recharts lib."""
|
||||
|
||||
from typing import Dict, Literal
|
||||
from typing import Literal
|
||||
|
||||
from reflex.components.component import Component, MemoizationLeaf, NoSSRComponent
|
||||
|
||||
@ -8,16 +8,16 @@ from reflex.components.component import Component, MemoizationLeaf, NoSSRCompone
|
||||
class Recharts(Component):
|
||||
"""A component that wraps a recharts lib."""
|
||||
|
||||
library = "recharts@2.13.0"
|
||||
library = "recharts@2.15.0"
|
||||
|
||||
def _get_style(self) -> Dict:
|
||||
def _get_style(self) -> dict:
|
||||
return {"wrapperStyle": self.style}
|
||||
|
||||
|
||||
class RechartsCharts(NoSSRComponent, MemoizationLeaf):
|
||||
"""A component that wraps a recharts lib."""
|
||||
|
||||
library = "recharts@2.13.0"
|
||||
library = "recharts@2.15.0"
|
||||
|
||||
|
||||
LiteralAnimationEasing = Literal["ease", "ease-in", "ease-out", "ease-in-out", "linear"]
|
||||
|
@ -167,7 +167,7 @@ class ToastProps(PropsBase, NoExtrasAllowedProps):
|
||||
class Toaster(Component):
|
||||
"""A Toaster Component for displaying toast notifications."""
|
||||
|
||||
library: str = "sonner@1.7.1"
|
||||
library: str = "sonner@1.7.2"
|
||||
|
||||
tag = "Toaster"
|
||||
|
||||
|
@ -390,7 +390,7 @@ class EnvVar(Generic[T]):
|
||||
os.environ[self.name] = str(value)
|
||||
|
||||
|
||||
class env_var: # type: ignore
|
||||
class env_var: # type: ignore # noqa: N801
|
||||
"""Descriptor for environment variables."""
|
||||
|
||||
name: str
|
||||
@ -556,9 +556,6 @@ class EnvironmentVariables:
|
||||
# Arguments to pass to the app harness driver.
|
||||
APP_HARNESS_DRIVER_ARGS: EnvVar[str] = env_var("")
|
||||
|
||||
# Where to save screenshots when tests fail.
|
||||
SCREENSHOT_DIR: EnvVar[Optional[Path]] = env_var(None)
|
||||
|
||||
# Whether to check for outdated package versions.
|
||||
REFLEX_CHECK_LATEST_VERSION: EnvVar[bool] = env_var(True)
|
||||
|
||||
@ -826,16 +823,16 @@ class Config(Base):
|
||||
if "api_url" not in self._non_default_attributes:
|
||||
# If running in Github Codespaces, override API_URL
|
||||
codespace_name = os.getenv("CODESPACE_NAME")
|
||||
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN = os.getenv(
|
||||
github_codespaces_port_forwarding_domain = os.getenv(
|
||||
"GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
|
||||
)
|
||||
# If running on Replit.com interactively, override API_URL to ensure we maintain the backend_port
|
||||
replit_dev_domain = os.getenv("REPLIT_DEV_DOMAIN")
|
||||
backend_port = kwargs.get("backend_port", self.backend_port)
|
||||
if codespace_name and GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN:
|
||||
if codespace_name and github_codespaces_port_forwarding_domain:
|
||||
self.api_url = (
|
||||
f"https://{codespace_name}-{kwargs.get('backend_port', self.backend_port)}"
|
||||
f".{GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN}"
|
||||
f".{github_codespaces_port_forwarding_domain}"
|
||||
)
|
||||
elif replit_dev_domain and backend_port:
|
||||
self.api_url = f"https://{replit_dev_domain}:{backend_port}"
|
||||
|
@ -28,6 +28,8 @@ class Ext(SimpleNamespace):
|
||||
ZIP = ".zip"
|
||||
# The extension for executable files on Windows.
|
||||
EXE = ".exe"
|
||||
# The extension for markdown files.
|
||||
MD = ".md"
|
||||
|
||||
|
||||
class CompileVars(SimpleNamespace):
|
||||
|
@ -37,10 +37,10 @@ class Bun(SimpleNamespace):
|
||||
"""Bun constants."""
|
||||
|
||||
# The Bun version.
|
||||
VERSION = "1.1.29"
|
||||
VERSION = "1.2.0"
|
||||
|
||||
# Min Bun Version
|
||||
MIN_VERSION = "0.7.0"
|
||||
MIN_VERSION = "1.1.0"
|
||||
|
||||
# URL to bun install script.
|
||||
INSTALL_URL = "https://raw.githubusercontent.com/reflex-dev/reflex/main/scripts/bun_install.sh"
|
||||
@ -178,21 +178,21 @@ class PackageJson(SimpleNamespace):
|
||||
PATH = "package.json"
|
||||
|
||||
DEPENDENCIES = {
|
||||
"@babel/standalone": "7.26.0",
|
||||
"@emotion/react": "11.13.3",
|
||||
"axios": "1.7.7",
|
||||
"@babel/standalone": "7.26.6",
|
||||
"@emotion/react": "11.14.0",
|
||||
"axios": "1.7.9",
|
||||
"json5": "2.2.3",
|
||||
"next": "15.1.4",
|
||||
"next": "15.1.6",
|
||||
"next-sitemap": "4.2.3",
|
||||
"next-themes": "0.4.3",
|
||||
"next-themes": "0.4.4",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-focus-lock": "2.13.2",
|
||||
"react-focus-lock": "2.13.5",
|
||||
"socket.io-client": "4.8.1",
|
||||
"universal-cookie": "7.2.2",
|
||||
}
|
||||
DEV_DEPENDENCIES = {
|
||||
"autoprefixer": "10.4.20",
|
||||
"postcss": "8.4.49",
|
||||
"postcss": "8.5.1",
|
||||
"postcss-import": "16.1.0",
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ class Tailwind(SimpleNamespace):
|
||||
"""Tailwind constants."""
|
||||
|
||||
# The Tailwindcss version
|
||||
VERSION = "tailwindcss@3.4.15"
|
||||
VERSION = "tailwindcss@3.4.17"
|
||||
# The Tailwind config.
|
||||
CONFIG = "tailwind.config.js"
|
||||
# Default Tailwind content paths
|
||||
|
@ -534,10 +534,10 @@ class JavasciptKeyboardEvent:
|
||||
"""Interface for a Javascript KeyboardEvent https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent."""
|
||||
|
||||
key: str = ""
|
||||
altKey: bool = False
|
||||
ctrlKey: bool = False
|
||||
metaKey: bool = False
|
||||
shiftKey: bool = False
|
||||
altKey: bool = False # noqa: N815
|
||||
ctrlKey: bool = False # noqa: N815
|
||||
metaKey: bool = False # noqa: N815
|
||||
shiftKey: bool = False # noqa: N815
|
||||
|
||||
|
||||
def input_event(e: Var[JavascriptInputEvent]) -> Tuple[Var[str]]:
|
||||
|
@ -34,6 +34,18 @@ def _client_state_ref(var_name: str) -> str:
|
||||
return f"refs['_client_state_{var_name}']"
|
||||
|
||||
|
||||
def _client_state_ref_dict(var_name: str) -> str:
|
||||
"""Get the ref path for a ClientStateVar.
|
||||
|
||||
Args:
|
||||
var_name: The name of the variable.
|
||||
|
||||
Returns:
|
||||
An accessor for ClientStateVar ref as a string.
|
||||
"""
|
||||
return f"refs['_client_state_dict_{var_name}']"
|
||||
|
||||
|
||||
@dataclasses.dataclass(
|
||||
eq=False,
|
||||
frozen=True,
|
||||
@ -115,10 +127,41 @@ class ClientStateVar(Var):
|
||||
"react": [ImportVar(tag="useState"), ImportVar(tag="useId")],
|
||||
}
|
||||
if global_ref:
|
||||
hooks[f"{_client_state_ref(var_name)} ??= {{}}"] = None
|
||||
hooks[f"{_client_state_ref(setter_name)} ??= {{}}"] = None
|
||||
hooks[f"{_client_state_ref(var_name)}[{id_name}] = {var_name}"] = None
|
||||
hooks[f"{_client_state_ref(setter_name)}[{id_name}] = {setter_name}"] = None
|
||||
arg_name = get_unique_variable_name()
|
||||
func = ArgsFunctionOperationBuilder.create(
|
||||
args_names=(arg_name,),
|
||||
return_expr=Var("Array.prototype.forEach.call")
|
||||
.to(FunctionVar)
|
||||
.call(
|
||||
(
|
||||
Var("Object.values")
|
||||
.to(FunctionVar)
|
||||
.call(Var(_client_state_ref_dict(setter_name)))
|
||||
.to(list)
|
||||
.to(list)
|
||||
)
|
||||
+ Var.create(
|
||||
[
|
||||
Var(
|
||||
f"(value) => {{ {_client_state_ref(var_name)} = value; }}"
|
||||
)
|
||||
]
|
||||
).to(list),
|
||||
ArgsFunctionOperationBuilder.create(
|
||||
args_names=("setter",),
|
||||
return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
hooks[f"{_client_state_ref(setter_name)} = {func!s}"] = None
|
||||
hooks[f"{_client_state_ref(var_name)} ??= {var_name!s}"] = None
|
||||
hooks[f"{_client_state_ref_dict(var_name)} ??= {{}}"] = None
|
||||
hooks[f"{_client_state_ref_dict(setter_name)} ??= {{}}"] = None
|
||||
hooks[f"{_client_state_ref_dict(var_name)}[{id_name}] = {var_name}"] = None
|
||||
hooks[
|
||||
f"{_client_state_ref_dict(setter_name)}[{id_name}] = {setter_name}"
|
||||
] = None
|
||||
imports.update(_refs_import)
|
||||
return cls(
|
||||
_js_expr="",
|
||||
@ -150,7 +193,7 @@ class ClientStateVar(Var):
|
||||
return (
|
||||
Var(
|
||||
_js_expr=(
|
||||
_client_state_ref(self._getter_name) + f"[{self._id_name}]"
|
||||
_client_state_ref_dict(self._getter_name) + f"[{self._id_name}]"
|
||||
if self._global_ref
|
||||
else self._getter_name
|
||||
),
|
||||
@ -179,26 +222,11 @@ class ClientStateVar(Var):
|
||||
"""
|
||||
_var_data = VarData(imports=_refs_import if self._global_ref else {})
|
||||
|
||||
arg_name = get_unique_variable_name()
|
||||
setter = (
|
||||
ArgsFunctionOperationBuilder.create(
|
||||
args_names=(arg_name,),
|
||||
return_expr=Var("Array.prototype.forEach.call")
|
||||
.to(FunctionVar)
|
||||
.call(
|
||||
Var("Object.values")
|
||||
.to(FunctionVar)
|
||||
.call(Var(_client_state_ref(self._setter_name))),
|
||||
ArgsFunctionOperationBuilder.create(
|
||||
args_names=("setter",),
|
||||
return_expr=Var("setter").to(FunctionVar).call(Var(arg_name)),
|
||||
),
|
||||
),
|
||||
_var_data=_var_data,
|
||||
)
|
||||
Var(_client_state_ref(self._setter_name))
|
||||
if self._global_ref
|
||||
else Var(self._setter_name, _var_data=_var_data).to(FunctionVar)
|
||||
)
|
||||
else Var(self._setter_name, _var_data=_var_data)
|
||||
).to(FunctionVar)
|
||||
|
||||
if value is not NoValue:
|
||||
# This is a hack to make it work like an EventSpec taking an arg
|
||||
|
@ -26,7 +26,7 @@ def const(name: str | list[str], value: str | Var) -> Var:
|
||||
return Var(_js_expr=f"const {name} = {value}")
|
||||
|
||||
|
||||
def useCallback(func: str, deps: list) -> Var:
|
||||
def useCallback(func: str, deps: list) -> Var: # noqa: N802
|
||||
"""Create a useCallback hook with a function and dependencies.
|
||||
|
||||
Args:
|
||||
@ -42,7 +42,7 @@ def useCallback(func: str, deps: list) -> Var:
|
||||
)
|
||||
|
||||
|
||||
def useContext(context: str) -> Var:
|
||||
def useContext(context: str) -> Var: # noqa: N802
|
||||
"""Create a useContext hook with a context.
|
||||
|
||||
Args:
|
||||
@ -57,7 +57,7 @@ def useContext(context: str) -> Var:
|
||||
)
|
||||
|
||||
|
||||
def useRef(default: str) -> Var:
|
||||
def useRef(default: str) -> Var: # noqa: N802
|
||||
"""Create a useRef hook with a default value.
|
||||
|
||||
Args:
|
||||
@ -72,7 +72,7 @@ def useRef(default: str) -> Var:
|
||||
)
|
||||
|
||||
|
||||
def useState(var_name: str, default: str | None = None) -> Var:
|
||||
def useState(var_name: str, default: str | None = None) -> Var: # noqa: N802
|
||||
"""Create a useState hook with a variable name and setter name.
|
||||
|
||||
Args:
|
||||
|
@ -109,7 +109,7 @@ class DrawerSidebar(DrawerRoot):
|
||||
snap_points: Optional[List[Union[float, str]]] = None,
|
||||
fade_from_index: Optional[Union[Var[int], int]] = None,
|
||||
scroll_lock_timeout: Optional[Union[Var[int], int]] = None,
|
||||
preventScrollRestoration: Optional[Union[Var[bool], bool]] = None,
|
||||
prevent_scroll_restoration: Optional[Union[Var[bool], bool]] = None,
|
||||
should_scale_background: Optional[Union[Var[bool], bool]] = None,
|
||||
close_threshold: Optional[Union[Var[float], float]] = None,
|
||||
as_child: Optional[Union[Var[bool], bool]] = None,
|
||||
|
@ -577,6 +577,20 @@ def deploy(
|
||||
)
|
||||
|
||||
|
||||
@cli.command()
|
||||
def rename(
|
||||
new_name: str = typer.Argument(..., help="The new name for the app."),
|
||||
loglevel: constants.LogLevel = typer.Option(
|
||||
config.loglevel, help="The log level to use."
|
||||
),
|
||||
):
|
||||
"""Rename the app in the current directory."""
|
||||
from reflex.utils import prerequisites
|
||||
|
||||
prerequisites.validate_app_name(new_name)
|
||||
prerequisites.rename_app(new_name, loglevel)
|
||||
|
||||
|
||||
cli.add_typer(db_cli, name="db", help="Subcommands for managing the database schema.")
|
||||
cli.add_typer(script_cli, name="script", help="Subcommands running helper scripts.")
|
||||
cli.add_typer(
|
||||
|
@ -94,14 +94,14 @@ from reflex.event import (
|
||||
)
|
||||
from reflex.utils import console, format, path_ops, prerequisites, types
|
||||
from reflex.utils.exceptions import (
|
||||
ComputedVarShadowsBaseVars,
|
||||
ComputedVarShadowsStateVar,
|
||||
DynamicComponentInvalidSignature,
|
||||
DynamicRouteArgShadowsStateVar,
|
||||
EventHandlerShadowsBuiltInStateMethod,
|
||||
ComputedVarShadowsBaseVarsError,
|
||||
ComputedVarShadowsStateVarError,
|
||||
DynamicComponentInvalidSignatureError,
|
||||
DynamicRouteArgShadowsStateVarError,
|
||||
EventHandlerShadowsBuiltInStateMethodError,
|
||||
ImmutableStateError,
|
||||
InvalidLockWarningThresholdError,
|
||||
InvalidStateManagerMode,
|
||||
InvalidStateManagerModeError,
|
||||
LockExpiredError,
|
||||
ReflexRuntimeError,
|
||||
SetUndefinedStateVarError,
|
||||
@ -816,7 +816,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"""Check for shadow methods and raise error if any.
|
||||
|
||||
Raises:
|
||||
EventHandlerShadowsBuiltInStateMethod: When an event handler shadows an inbuilt state method.
|
||||
EventHandlerShadowsBuiltInStateMethodError: When an event handler shadows an inbuilt state method.
|
||||
"""
|
||||
overridden_methods = set()
|
||||
state_base_functions = cls._get_base_functions()
|
||||
@ -830,7 +830,7 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
overridden_methods.add(method.__name__)
|
||||
|
||||
for method_name in overridden_methods:
|
||||
raise EventHandlerShadowsBuiltInStateMethod(
|
||||
raise EventHandlerShadowsBuiltInStateMethodError(
|
||||
f"The event handler name `{method_name}` shadows a builtin State method; use a different name instead"
|
||||
)
|
||||
|
||||
@ -839,11 +839,11 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"""Check for shadow base vars and raise error if any.
|
||||
|
||||
Raises:
|
||||
ComputedVarShadowsBaseVars: When a computed var shadows a base var.
|
||||
ComputedVarShadowsBaseVarsError: When a computed var shadows a base var.
|
||||
"""
|
||||
for computed_var_ in cls._get_computed_vars():
|
||||
if computed_var_._js_expr in cls.__annotations__:
|
||||
raise ComputedVarShadowsBaseVars(
|
||||
raise ComputedVarShadowsBaseVarsError(
|
||||
f"The computed var name `{computed_var_._js_expr}` shadows a base var in {cls.__module__}.{cls.__name__}; use a different name instead"
|
||||
)
|
||||
|
||||
@ -852,14 +852,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
"""Check for shadow computed vars and raise error if any.
|
||||
|
||||
Raises:
|
||||
ComputedVarShadowsStateVar: When a computed var shadows another.
|
||||
ComputedVarShadowsStateVarError: When a computed var shadows another.
|
||||
"""
|
||||
for name, cv in cls.__dict__.items():
|
||||
if not is_computed_var(cv):
|
||||
continue
|
||||
name = cv._js_expr
|
||||
if name in cls.inherited_vars or name in cls.inherited_backend_vars:
|
||||
raise ComputedVarShadowsStateVar(
|
||||
raise ComputedVarShadowsStateVarError(
|
||||
f"The computed var name `{cv._js_expr}` shadows a var in {cls.__module__}.{cls.__name__}; use a different name instead"
|
||||
)
|
||||
|
||||
@ -1219,14 +1219,14 @@ class BaseState(Base, ABC, extra=pydantic.Extra.allow):
|
||||
args: a dict of args
|
||||
|
||||
Raises:
|
||||
DynamicRouteArgShadowsStateVar: If a dynamic arg is shadowing an existing var.
|
||||
DynamicRouteArgShadowsStateVarError: If a dynamic arg is shadowing an existing var.
|
||||
"""
|
||||
for arg in args:
|
||||
if (
|
||||
arg in cls.computed_vars
|
||||
and not isinstance(cls.computed_vars[arg], DynamicRouteVar)
|
||||
) or arg in cls.base_vars:
|
||||
raise DynamicRouteArgShadowsStateVar(
|
||||
raise DynamicRouteArgShadowsStateVarError(
|
||||
f"Dynamic route arg '{arg}' is shadowing an existing var in {cls.__module__}.{cls.__name__}"
|
||||
)
|
||||
for substate in cls.get_substates():
|
||||
@ -2354,8 +2354,7 @@ def dynamic(func: Callable[[T], Component]):
|
||||
The dynamically generated component.
|
||||
|
||||
Raises:
|
||||
DynamicComponentInvalidSignature: If the function does not have exactly one parameter.
|
||||
DynamicComponentInvalidSignature: If the function does not have a type hint for the state class.
|
||||
DynamicComponentInvalidSignatureError: If the function does not have exactly one parameter or a type hint for the state class.
|
||||
"""
|
||||
number_of_parameters = len(inspect.signature(func).parameters)
|
||||
|
||||
@ -2367,12 +2366,12 @@ def dynamic(func: Callable[[T], Component]):
|
||||
values = list(func_signature.values())
|
||||
|
||||
if number_of_parameters != 1:
|
||||
raise DynamicComponentInvalidSignature(
|
||||
raise DynamicComponentInvalidSignatureError(
|
||||
"The function must have exactly one parameter, which is the state class."
|
||||
)
|
||||
|
||||
if len(values) != 1:
|
||||
raise DynamicComponentInvalidSignature(
|
||||
raise DynamicComponentInvalidSignatureError(
|
||||
"You must provide a type hint for the state class in the function."
|
||||
)
|
||||
|
||||
@ -2878,7 +2877,7 @@ class StateManager(Base, ABC):
|
||||
state: The state class to use.
|
||||
|
||||
Raises:
|
||||
InvalidStateManagerMode: If the state manager mode is invalid.
|
||||
InvalidStateManagerModeError: If the state manager mode is invalid.
|
||||
|
||||
Returns:
|
||||
The state manager (either disk, memory or redis).
|
||||
@ -2901,7 +2900,7 @@ class StateManager(Base, ABC):
|
||||
lock_expiration=config.redis_lock_expiration,
|
||||
lock_warning_threshold=config.redis_lock_warning_threshold,
|
||||
)
|
||||
raise InvalidStateManagerMode(
|
||||
raise InvalidStateManagerModeError(
|
||||
f"Expected one of: DISK, MEMORY, REDIS, got {config.state_manager_mode}"
|
||||
)
|
||||
|
||||
@ -4063,10 +4062,10 @@ def serialize_mutable_proxy(mp: MutableProxy):
|
||||
return mp.__wrapped__
|
||||
|
||||
|
||||
_orig_json_JSONEncoder_default = json.JSONEncoder.default
|
||||
_orig_json_encoder_default = json.JSONEncoder.default
|
||||
|
||||
|
||||
def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
|
||||
def _json_encoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
|
||||
"""Wrap JSONEncoder.default to handle MutableProxy objects.
|
||||
|
||||
Args:
|
||||
@ -4080,10 +4079,10 @@ def _json_JSONEncoder_default_wrapper(self: json.JSONEncoder, o: Any) -> Any:
|
||||
return o.__wrapped__
|
||||
except AttributeError:
|
||||
pass
|
||||
return _orig_json_JSONEncoder_default(self, o)
|
||||
return _orig_json_encoder_default(self, o)
|
||||
|
||||
|
||||
json.JSONEncoder.default = _json_JSONEncoder_default_wrapper
|
||||
json.JSONEncoder.default = _json_encoder_default_wrapper
|
||||
|
||||
|
||||
class ImmutableMutableProxy(MutableProxy):
|
||||
|
@ -87,7 +87,7 @@ else:
|
||||
|
||||
|
||||
# borrowed from py3.11
|
||||
class chdir(contextlib.AbstractContextManager):
|
||||
class chdir(contextlib.AbstractContextManager): # noqa: N801
|
||||
"""Non thread-safe context manager to change the current working directory."""
|
||||
|
||||
def __init__(self, path: str | Path):
|
||||
@ -296,7 +296,7 @@ class AppHarness:
|
||||
self.app_instance = self.app_module.app
|
||||
if isinstance(self.app_instance._state_manager, StateManagerRedis):
|
||||
# Create our own redis connection for testing.
|
||||
self.state_manager = StateManagerRedis.create(self.app_instance.state)
|
||||
self.state_manager = StateManagerRedis.create(self.app_instance._state)
|
||||
else:
|
||||
self.state_manager = self.app_instance._state_manager
|
||||
|
||||
@ -324,7 +324,7 @@ class AppHarness:
|
||||
return _shutdown_redis
|
||||
|
||||
def _start_backend(self, port: int = 0):
|
||||
if self.app_instance is None:
|
||||
if self.app_instance is None or self.app_instance.api is None:
|
||||
raise RuntimeError("App was not initialized.")
|
||||
self.backend = uvicorn.Server(
|
||||
uvicorn.Config(
|
||||
@ -353,12 +353,12 @@ class AppHarness:
|
||||
self.app_instance.state_manager,
|
||||
StateManagerRedis,
|
||||
)
|
||||
and self.app_instance.state is not None
|
||||
and self.app_instance._state is not None
|
||||
):
|
||||
with contextlib.suppress(RuntimeError):
|
||||
await self.app_instance.state_manager.close()
|
||||
self.app_instance._state_manager = StateManagerRedis.create(
|
||||
state=self.app_instance.state,
|
||||
state=self.app_instance._state,
|
||||
)
|
||||
if not isinstance(self.app_instance.state_manager, StateManagerRedis):
|
||||
raise RuntimeError("Failed to reset state manager.")
|
||||
|
@ -42,10 +42,7 @@ def codespaces_port_forwarding_domain() -> str | None:
|
||||
Returns:
|
||||
The domain for port forwarding in Github Codespaces, or None if not running in Codespaces.
|
||||
"""
|
||||
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN = os.getenv(
|
||||
"GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN"
|
||||
)
|
||||
return GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN
|
||||
return os.getenv("GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN")
|
||||
|
||||
|
||||
def is_running_in_codespaces() -> bool:
|
||||
|
@ -11,7 +11,7 @@ class ConfigError(ReflexError):
|
||||
"""Custom exception for config related errors."""
|
||||
|
||||
|
||||
class InvalidStateManagerMode(ReflexError, ValueError):
|
||||
class InvalidStateManagerModeError(ReflexError, ValueError):
|
||||
"""Raised when an invalid state manager mode is provided."""
|
||||
|
||||
|
||||
@ -143,35 +143,35 @@ class EventFnArgMismatchError(ReflexError, TypeError):
|
||||
"""Raised when the number of args required by an event handler is more than provided by the event trigger."""
|
||||
|
||||
|
||||
class DynamicRouteArgShadowsStateVar(ReflexError, NameError):
|
||||
class DynamicRouteArgShadowsStateVarError(ReflexError, NameError):
|
||||
"""Raised when a dynamic route arg shadows a state var."""
|
||||
|
||||
|
||||
class ComputedVarShadowsStateVar(ReflexError, NameError):
|
||||
class ComputedVarShadowsStateVarError(ReflexError, NameError):
|
||||
"""Raised when a computed var shadows a state var."""
|
||||
|
||||
|
||||
class ComputedVarShadowsBaseVars(ReflexError, NameError):
|
||||
class ComputedVarShadowsBaseVarsError(ReflexError, NameError):
|
||||
"""Raised when a computed var shadows a base var."""
|
||||
|
||||
|
||||
class EventHandlerShadowsBuiltInStateMethod(ReflexError, NameError):
|
||||
class EventHandlerShadowsBuiltInStateMethodError(ReflexError, NameError):
|
||||
"""Raised when an event handler shadows a built-in state method."""
|
||||
|
||||
|
||||
class GeneratedCodeHasNoFunctionDefs(ReflexError):
|
||||
class GeneratedCodeHasNoFunctionDefsError(ReflexError):
|
||||
"""Raised when refactored code generated with flexgen has no functions defined."""
|
||||
|
||||
|
||||
class PrimitiveUnserializableToJSON(ReflexError, ValueError):
|
||||
class PrimitiveUnserializableToJSONError(ReflexError, ValueError):
|
||||
"""Raised when a primitive type is unserializable to JSON. Usually with NaN and Infinity."""
|
||||
|
||||
|
||||
class InvalidLifespanTaskType(ReflexError, TypeError):
|
||||
class InvalidLifespanTaskTypeError(ReflexError, TypeError):
|
||||
"""Raised when an invalid task type is registered as a lifespan task."""
|
||||
|
||||
|
||||
class DynamicComponentMissingLibrary(ReflexError, ValueError):
|
||||
class DynamicComponentMissingLibraryError(ReflexError, ValueError):
|
||||
"""Raised when a dynamic component is missing a library."""
|
||||
|
||||
|
||||
@ -187,7 +187,7 @@ class EnvironmentVarValueError(ReflexError, ValueError):
|
||||
"""Raised when an environment variable is set to an invalid value."""
|
||||
|
||||
|
||||
class DynamicComponentInvalidSignature(ReflexError, TypeError):
|
||||
class DynamicComponentInvalidSignatureError(ReflexError, TypeError):
|
||||
"""Raised when a dynamic component has an invalid signature."""
|
||||
|
||||
|
||||
|
@ -364,11 +364,11 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
||||
|
||||
app_module = get_app_module()
|
||||
|
||||
RUN_BACKEND_PROD = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
|
||||
RUN_BACKEND_PROD_WINDOWS = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
|
||||
run_backend_prod = f"gunicorn --worker-class {config.gunicorn_worker_class} --max-requests {config.gunicorn_max_requests} --max-requests-jitter {config.gunicorn_max_requests_jitter} --preload --timeout {config.timeout} --log-level critical".split()
|
||||
run_backend_prod_windows = f"uvicorn --limit-max-requests {config.gunicorn_max_requests} --timeout-keep-alive {config.timeout}".split()
|
||||
command = (
|
||||
[
|
||||
*RUN_BACKEND_PROD_WINDOWS,
|
||||
*run_backend_prod_windows,
|
||||
"--host",
|
||||
host,
|
||||
"--port",
|
||||
@ -377,7 +377,7 @@ def run_uvicorn_backend_prod(host: str, port: int, loglevel: LogLevel):
|
||||
]
|
||||
if constants.IS_WINDOWS
|
||||
else [
|
||||
*RUN_BACKEND_PROD,
|
||||
*run_backend_prod,
|
||||
"--bind",
|
||||
f"{host}:{port}",
|
||||
"--threads",
|
||||
|
@ -7,6 +7,7 @@ import dataclasses
|
||||
import functools
|
||||
import importlib
|
||||
import importlib.metadata
|
||||
import importlib.util
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
@ -37,7 +38,7 @@ from reflex.compiler import templates
|
||||
from reflex.config import Config, environment, get_config
|
||||
from reflex.utils import console, net, path_ops, processes, redir
|
||||
from reflex.utils.exceptions import (
|
||||
GeneratedCodeHasNoFunctionDefs,
|
||||
GeneratedCodeHasNoFunctionDefsError,
|
||||
SystemPackageMissingError,
|
||||
)
|
||||
from reflex.utils.format import format_library_name
|
||||
@ -463,6 +464,167 @@ def validate_app_name(app_name: str | None = None) -> str:
|
||||
return app_name
|
||||
|
||||
|
||||
def rename_path_up_tree(full_path: str | Path, old_name: str, new_name: str) -> Path:
|
||||
"""Rename all instances of `old_name` in the path (file and directories) to `new_name`.
|
||||
The renaming stops when we reach the directory containing `rxconfig.py`.
|
||||
|
||||
Args:
|
||||
full_path: The full path to start renaming from.
|
||||
old_name: The name to be replaced.
|
||||
new_name: The replacement name.
|
||||
|
||||
Returns:
|
||||
The updated path after renaming.
|
||||
"""
|
||||
current_path = Path(full_path)
|
||||
new_path = None
|
||||
|
||||
while True:
|
||||
directory, base = current_path.parent, current_path.name
|
||||
# Stop renaming when we reach the root dir (which contains rxconfig.py)
|
||||
if current_path.is_dir() and (current_path / "rxconfig.py").exists():
|
||||
new_path = current_path
|
||||
break
|
||||
|
||||
if old_name == base.removesuffix(constants.Ext.PY):
|
||||
new_base = base.replace(old_name, new_name)
|
||||
new_path = directory / new_base
|
||||
current_path.rename(new_path)
|
||||
console.debug(f"Renamed {current_path} -> {new_path}")
|
||||
current_path = new_path
|
||||
else:
|
||||
new_path = current_path
|
||||
|
||||
# Move up the directory tree
|
||||
current_path = directory
|
||||
|
||||
return new_path
|
||||
|
||||
|
||||
def rename_app(new_app_name: str, loglevel: constants.LogLevel):
|
||||
"""Rename the app directory.
|
||||
|
||||
Args:
|
||||
new_app_name: The new name for the app.
|
||||
loglevel: The log level to use.
|
||||
|
||||
Raises:
|
||||
Exit: If the command is not ran in the root dir or the app module cannot be imported.
|
||||
"""
|
||||
# Set the log level.
|
||||
console.set_log_level(loglevel)
|
||||
|
||||
if not constants.Config.FILE.exists():
|
||||
console.error(
|
||||
"No rxconfig.py found. Make sure you are in the root directory of your app."
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
|
||||
sys.path.insert(0, str(Path.cwd()))
|
||||
|
||||
config = get_config()
|
||||
module_path = importlib.util.find_spec(config.module)
|
||||
if module_path is None:
|
||||
console.error(f"Could not find module {config.module}.")
|
||||
raise typer.Exit(1)
|
||||
|
||||
if not module_path.origin:
|
||||
console.error(f"Could not find origin for module {config.module}.")
|
||||
raise typer.Exit(1)
|
||||
console.info(f"Renaming app directory to {new_app_name}.")
|
||||
process_directory(
|
||||
Path.cwd(),
|
||||
config.app_name,
|
||||
new_app_name,
|
||||
exclude_dirs=[constants.Dirs.WEB, constants.Dirs.APP_ASSETS],
|
||||
)
|
||||
|
||||
rename_path_up_tree(Path(module_path.origin), config.app_name, new_app_name)
|
||||
|
||||
console.success(f"App directory renamed to [bold]{new_app_name}[/bold].")
|
||||
|
||||
|
||||
def rename_imports_and_app_name(file_path: str | Path, old_name: str, new_name: str):
|
||||
"""Rename imports the file using string replacement as well as app_name in rxconfig.py.
|
||||
|
||||
Args:
|
||||
file_path: The file to process.
|
||||
old_name: The old name to replace.
|
||||
new_name: The new name to use.
|
||||
"""
|
||||
file_path = Path(file_path)
|
||||
content = file_path.read_text()
|
||||
|
||||
# Replace `from old_name.` or `from old_name` with `from new_name`
|
||||
content = re.sub(
|
||||
rf"\bfrom {re.escape(old_name)}(\b|\.|\s)",
|
||||
lambda match: f"from {new_name}{match.group(1)}",
|
||||
content,
|
||||
)
|
||||
|
||||
# Replace `import old_name` with `import new_name`
|
||||
content = re.sub(
|
||||
rf"\bimport {re.escape(old_name)}\b",
|
||||
f"import {new_name}",
|
||||
content,
|
||||
)
|
||||
|
||||
# Replace `app_name="old_name"` in rx.Config
|
||||
content = re.sub(
|
||||
rf'\bapp_name\s*=\s*["\']{re.escape(old_name)}["\']',
|
||||
f'app_name="{new_name}"',
|
||||
content,
|
||||
)
|
||||
|
||||
# Replace positional argument `"old_name"` in rx.Config
|
||||
content = re.sub(
|
||||
rf'\brx\.Config\(\s*["\']{re.escape(old_name)}["\']',
|
||||
f'rx.Config("{new_name}"',
|
||||
content,
|
||||
)
|
||||
|
||||
file_path.write_text(content)
|
||||
|
||||
|
||||
def process_directory(
|
||||
directory: str | Path,
|
||||
old_name: str,
|
||||
new_name: str,
|
||||
exclude_dirs: list | None = None,
|
||||
extensions: list | None = None,
|
||||
):
|
||||
"""Process files with specified extensions in a directory, excluding specified directories.
|
||||
|
||||
Args:
|
||||
directory: The root directory to process.
|
||||
old_name: The old name to replace.
|
||||
new_name: The new name to use.
|
||||
exclude_dirs: List of directory names to exclude. Defaults to None.
|
||||
extensions: List of file extensions to process.
|
||||
"""
|
||||
exclude_dirs = exclude_dirs or []
|
||||
extensions = extensions or [
|
||||
constants.Ext.PY,
|
||||
constants.Ext.MD,
|
||||
] # include .md files, typically used in reflex-web.
|
||||
extensions_set = {ext.lstrip(".") for ext in extensions}
|
||||
directory = Path(directory)
|
||||
|
||||
root_exclude_dirs = {directory / exclude_dir for exclude_dir in exclude_dirs}
|
||||
|
||||
files = (
|
||||
p.resolve()
|
||||
for p in directory.glob("**/*")
|
||||
if p.is_file() and p.suffix.lstrip(".") in extensions_set
|
||||
)
|
||||
|
||||
for file_path in files:
|
||||
if not any(
|
||||
file_path.is_relative_to(exclude_dir) for exclude_dir in root_exclude_dirs
|
||||
):
|
||||
rename_imports_and_app_name(file_path, old_name, new_name)
|
||||
|
||||
|
||||
def create_config(app_name: str):
|
||||
"""Create a new rxconfig file.
|
||||
|
||||
@ -923,6 +1085,7 @@ def install_bun():
|
||||
constants.Bun.INSTALL_URL,
|
||||
f"bun-v{constants.Bun.VERSION}",
|
||||
BUN_INSTALL=str(constants.Bun.ROOT_PATH),
|
||||
BUN_VERSION=str(constants.Bun.VERSION),
|
||||
)
|
||||
|
||||
|
||||
@ -1657,7 +1820,7 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
|
||||
generation_hash: The generation hash from reflex.build.
|
||||
|
||||
Raises:
|
||||
GeneratedCodeHasNoFunctionDefs: If the fetched code has no function definitions
|
||||
GeneratedCodeHasNoFunctionDefsError: If the fetched code has no function definitions
|
||||
(the refactored reflex code is expected to have at least one root function defined).
|
||||
"""
|
||||
# Download the reflex code for the generation.
|
||||
@ -1674,7 +1837,7 @@ def initialize_main_module_index_from_generation(app_name: str, generation_hash:
|
||||
# Determine the name of the last function, which renders the generated code.
|
||||
defined_funcs = re.findall(r"def ([a-zA-Z_]+)\(", resp.text)
|
||||
if not defined_funcs:
|
||||
raise GeneratedCodeHasNoFunctionDefs(
|
||||
raise GeneratedCodeHasNoFunctionDefsError(
|
||||
f"No function definitions found in generated code from {url!r}."
|
||||
)
|
||||
render_func_name = defined_funcs[-1]
|
||||
|
@ -1565,7 +1565,7 @@ def figure_out_type(value: Any) -> types.GenericType:
|
||||
return type(value)
|
||||
|
||||
|
||||
class cached_property_no_lock(functools.cached_property):
|
||||
class cached_property_no_lock(functools.cached_property): # noqa: N801
|
||||
"""A special version of functools.cached_property that does not use a lock."""
|
||||
|
||||
def __init__(self, func: Callable):
|
||||
|
@ -18,7 +18,7 @@ from typing import (
|
||||
)
|
||||
|
||||
from reflex.constants.base import Dirs
|
||||
from reflex.utils.exceptions import PrimitiveUnserializableToJSON, VarTypeError
|
||||
from reflex.utils.exceptions import PrimitiveUnserializableToJSONError, VarTypeError
|
||||
from reflex.utils.imports import ImportDict, ImportVar
|
||||
|
||||
from .base import (
|
||||
@ -987,10 +987,10 @@ class LiteralNumberVar(LiteralVar, NumberVar):
|
||||
The JSON representation of the var.
|
||||
|
||||
Raises:
|
||||
PrimitiveUnserializableToJSON: If the var is unserializable to JSON.
|
||||
PrimitiveUnserializableToJSONError: If the var is unserializable to JSON.
|
||||
"""
|
||||
if math.isinf(self._var_value) or math.isnan(self._var_value):
|
||||
raise PrimitiveUnserializableToJSON(
|
||||
raise PrimitiveUnserializableToJSONError(
|
||||
f"No valid JSON representation for {self}"
|
||||
)
|
||||
return json.dumps(self._var_value)
|
||||
|
@ -78,6 +78,14 @@ case $platform in
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$target" in
|
||||
'linux'*)
|
||||
if [ -f /etc/alpine-release ]; then
|
||||
target="$target-musl"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $target = darwin-x64 ]]; then
|
||||
# Is this process running in Rosetta?
|
||||
# redirect stderr to devnull to avoid error message when not running in Rosetta
|
||||
@ -91,19 +99,20 @@ GITHUB=${GITHUB-"https://github.com"}
|
||||
|
||||
github_repo="$GITHUB/oven-sh/bun"
|
||||
|
||||
if [[ $target = darwin-x64 ]]; then
|
||||
# If AVX2 isn't supported, use the -baseline build
|
||||
# If AVX2 isn't supported, use the -baseline build
|
||||
case "$target" in
|
||||
'darwin-x64'*)
|
||||
if [[ $(sysctl -a | grep machdep.cpu | grep AVX2) == '' ]]; then
|
||||
target=darwin-x64-baseline
|
||||
target="$target-baseline"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ $target = linux-x64 ]]; then
|
||||
;;
|
||||
'linux-x64'*)
|
||||
# If AVX2 isn't supported, use the -baseline build
|
||||
if [[ $(cat /proc/cpuinfo | grep avx2) = '' ]]; then
|
||||
target=linux-x64-baseline
|
||||
target="$target-baseline"
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
exe_name=bun
|
||||
|
||||
@ -113,8 +122,10 @@ if [[ $# = 2 && $2 = debug-info ]]; then
|
||||
info "You requested a debug build of bun. More information will be shown if a crash occurs."
|
||||
fi
|
||||
|
||||
bun_version=BUN_VERSION
|
||||
|
||||
if [[ $# = 0 ]]; then
|
||||
bun_uri=$github_repo/releases/latest/download/bun-$target.zip
|
||||
bun_uri=$github_repo/releases/download/bun-v$bun_version/bun-$target.zip
|
||||
else
|
||||
bun_uri=$github_repo/releases/download/$1/bun-$target.zip
|
||||
fi
|
||||
|
@ -214,8 +214,12 @@ function Install-Bun {
|
||||
# http://community.sqlbackupandftp.com/t/error-1073741515-solved/1305
|
||||
if (($LASTEXITCODE -eq 3221225781) -or ($LASTEXITCODE -eq -1073741515)) # STATUS_DLL_NOT_FOUND
|
||||
{
|
||||
# TODO: as of July 2024, Bun has no external dependencies.
|
||||
# I want to keep this error message in for a few months to ensure that
|
||||
# if someone somehow runs into this, it can be reported.
|
||||
Write-Output "Install Failed - You are missing a DLL required to run bun.exe"
|
||||
Write-Output "This can be solved by installing the Visual C++ Redistributable from Microsoft:`nSee https://learn.microsoft.com/cpp/windows/latest-supported-vc-redist`nDirect Download -> https://aka.ms/vs/17/release/vc_redist.x64.exe`n`n"
|
||||
Write-Output "The error above should be unreachable as Bun does not depend on this library. Please comment in https://github.com/oven-sh/bun/issues/8598 or open a new issue.`n`n"
|
||||
Write-Output "The command '${BunBin}\bun.exe --revision' exited with code ${LASTEXITCODE}`n"
|
||||
return 1
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
"""Shared conftest for all integration tests."""
|
||||
|
||||
import os
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
@ -36,34 +34,6 @@ def xvfb():
|
||||
yield None
|
||||
|
||||
|
||||
def pytest_exception_interact(node, call, report):
|
||||
"""Take and upload screenshot when tests fail.
|
||||
|
||||
Args:
|
||||
node: The pytest item that failed.
|
||||
call: The pytest call describing when/where the test was invoked.
|
||||
report: The pytest log report object.
|
||||
"""
|
||||
screenshot_dir = environment.SCREENSHOT_DIR.get()
|
||||
if DISPLAY is None or screenshot_dir is None:
|
||||
return
|
||||
|
||||
screenshot_dir = Path(screenshot_dir)
|
||||
screenshot_dir.mkdir(parents=True, exist_ok=True)
|
||||
safe_filename = re.sub(
|
||||
r"(?u)[^-\w.]",
|
||||
"_",
|
||||
str(node.nodeid).strip().replace(" ", "_").replace(":", "_").replace(".py", ""),
|
||||
)
|
||||
|
||||
try:
|
||||
DISPLAY.waitgrab().save(
|
||||
(Path(screenshot_dir) / safe_filename).with_suffix(".png"),
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"Failed to take screenshot for {node}: {e}")
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"]
|
||||
)
|
||||
|
@ -172,7 +172,7 @@ def BackgroundTask():
|
||||
rx.button("Reset", on_click=State.reset_counter, id="reset"),
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
@ -288,7 +288,7 @@ def test_background_task(
|
||||
assert background_task._poll_for(lambda: counter.text == "620", timeout=40)
|
||||
# all tasks should have exited and cleaned up
|
||||
assert background_task._poll_for(
|
||||
lambda: not background_task.app_instance.background_tasks # type: ignore
|
||||
lambda: not background_task.app_instance._background_tasks # type: ignore
|
||||
)
|
||||
|
||||
|
||||
|
@ -188,7 +188,7 @@ def CallScript():
|
||||
yield rx.call_script("inline_counter = 0; external_counter = 0")
|
||||
self.reset()
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
Path("assets/external.js").write_text(external_scripts)
|
||||
|
||||
@app.add_page
|
||||
|
@ -127,7 +127,7 @@ def ClientSide():
|
||||
rx.box(ClientSideSubSubState.s1s, id="s1s"),
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
app.add_page(index, route="/foo")
|
||||
|
||||
@ -321,6 +321,7 @@ async def test_client_side_state(
|
||||
assert not driver.get_cookies()
|
||||
local_storage_items = local_storage.items()
|
||||
local_storage_items.pop("last_compiled_time", None)
|
||||
local_storage_items.pop("theme", None)
|
||||
assert not local_storage_items
|
||||
|
||||
# set some cookies and local storage values
|
||||
@ -436,6 +437,7 @@ async def test_client_side_state(
|
||||
|
||||
local_storage_items = local_storage.items()
|
||||
local_storage_items.pop("last_compiled_time", None)
|
||||
local_storage_items.pop("theme", None)
|
||||
assert local_storage_items.pop(f"{sub_state_name}.l1") == "l1 value"
|
||||
assert local_storage_items.pop(f"{sub_state_name}.l2") == "l2 value"
|
||||
assert local_storage_items.pop("l3") == "l3 value"
|
||||
|
@ -72,7 +72,7 @@ def ComponentStateApp():
|
||||
State=_Counter,
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State) # noqa
|
||||
app = rx.App(_state=rx.State) # noqa
|
||||
|
||||
@rx.page()
|
||||
def index():
|
||||
|
@ -36,7 +36,7 @@ def ConnectionBanner():
|
||||
rx.button("Delay", id="delay", on_click=State.delay),
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
|
@ -26,7 +26,7 @@ def DeployUrlSample() -> None:
|
||||
rx.button("GOTO SELF", on_click=State.goto_self, id="goto_self")
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
|
@ -138,7 +138,7 @@ def DynamicRoute():
|
||||
def redirect_page():
|
||||
return rx.fragment(rx.text("redirecting..."))
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index, route="/page/[page_id]", on_load=DynamicState.on_load) # type: ignore
|
||||
app.add_page(index, route="/static/x", on_load=DynamicState.on_load) # type: ignore
|
||||
app.add_page(index)
|
||||
|
@ -156,7 +156,7 @@ def TestEventAction():
|
||||
on_click=EventActionState.on_click("outer"), # type: ignore
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
|
@ -144,7 +144,7 @@ def EventChain():
|
||||
time.sleep(0.5)
|
||||
self.interim_value = "final"
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
token_input = rx.input(
|
||||
value=State.router.session.client_token, is_read_only=True, id="token"
|
||||
|
@ -39,7 +39,7 @@ def TestApp():
|
||||
"""
|
||||
print(1 / number)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -30,7 +30,7 @@ def FormSubmit(form_component):
|
||||
def form_submit(self, form_data: Dict):
|
||||
self.form_data = form_data
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
@ -90,7 +90,7 @@ def FormSubmitName(form_component):
|
||||
def form_submit(self, form_data: Dict):
|
||||
self.form_data = form_data
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -16,7 +16,7 @@ def FullyControlledInput():
|
||||
class State(rx.State):
|
||||
text: str = "initial"
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -45,7 +45,7 @@ def LoginSample():
|
||||
rx.button("Do it", on_click=State.login, id="doit"),
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
app.add_page(login)
|
||||
|
||||
|
@ -38,7 +38,7 @@ def ServerSideEvent():
|
||||
def set_value_return_c(self):
|
||||
return rx.set_value("c", "")
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -166,7 +166,7 @@ def UploadFile():
|
||||
rx.text(UploadState.event_order.to_string(), id="event-order"),
|
||||
)
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
app.add_page(index)
|
||||
|
||||
|
||||
|
@ -39,7 +39,7 @@ def VarOperations():
|
||||
dict2: Dict[int, int] = {3: 4}
|
||||
html_str: str = "<div>hello</div>"
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@rx.memo
|
||||
def memo_comp(list1: List[int], int_var1: int, id: str):
|
||||
|
@ -16,7 +16,7 @@ def DatetimeOperationsApp():
|
||||
date2: datetime = datetime(2031, 1, 1)
|
||||
date3: datetime = datetime(2021, 1, 1)
|
||||
|
||||
app = rx.App(state=DtOperationsState)
|
||||
app = rx.App(_state=DtOperationsState)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -20,7 +20,7 @@ def Table():
|
||||
"""App using table component."""
|
||||
import reflex as rx
|
||||
|
||||
app = rx.App(state=rx.State)
|
||||
app = rx.App(_state=rx.State)
|
||||
|
||||
@app.add_page
|
||||
def index():
|
||||
|
@ -1,11 +1,8 @@
|
||||
"""Test fixtures."""
|
||||
|
||||
import asyncio
|
||||
import contextlib
|
||||
import os
|
||||
import platform
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import Dict, Generator, Type
|
||||
from unittest import mock
|
||||
|
||||
@ -14,6 +11,7 @@ import pytest
|
||||
from reflex.app import App
|
||||
from reflex.event import EventSpec
|
||||
from reflex.model import ModelRegistry
|
||||
from reflex.testing import chdir
|
||||
from reflex.utils import prerequisites
|
||||
|
||||
from .states import (
|
||||
@ -191,33 +189,6 @@ def router_data(router_data_headers) -> Dict[str, str]:
|
||||
}
|
||||
|
||||
|
||||
# borrowed from py3.11
|
||||
class chdir(contextlib.AbstractContextManager):
|
||||
"""Non thread-safe context manager to change the current working directory."""
|
||||
|
||||
def __init__(self, path):
|
||||
"""Prepare contextmanager.
|
||||
|
||||
Args:
|
||||
path: the path to change to
|
||||
"""
|
||||
self.path = path
|
||||
self._old_cwd = []
|
||||
|
||||
def __enter__(self):
|
||||
"""Save current directory and perform chdir."""
|
||||
self._old_cwd.append(Path.cwd())
|
||||
os.chdir(self.path)
|
||||
|
||||
def __exit__(self, *excinfo):
|
||||
"""Change back to previous directory on stack.
|
||||
|
||||
Args:
|
||||
excinfo: sys.exc_info captured in the context block
|
||||
"""
|
||||
os.chdir(self._old_cwd.pop())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_working_dir(tmp_path):
|
||||
"""Create a temporary directory and chdir to it.
|
||||
|
@ -41,7 +41,7 @@ async def test_preprocess_no_events(hydrate_middleware, event1, mocker):
|
||||
mocker.patch("reflex.state.State.class_subclasses", {TestState})
|
||||
state = State()
|
||||
update = await hydrate_middleware.preprocess(
|
||||
app=App(state=State),
|
||||
app=App(_state=State),
|
||||
event=event1,
|
||||
state=state,
|
||||
)
|
||||
|
@ -236,14 +236,14 @@ def test_add_page_default_route(app: App, index_page, about_page):
|
||||
index_page: The index page.
|
||||
about_page: The about page.
|
||||
"""
|
||||
assert app.pages == {}
|
||||
assert app.unevaluated_pages == {}
|
||||
assert app._pages == {}
|
||||
assert app._unevaluated_pages == {}
|
||||
app.add_page(index_page)
|
||||
app._compile_page("index")
|
||||
assert app.pages.keys() == {"index"}
|
||||
assert app._pages.keys() == {"index"}
|
||||
app.add_page(about_page)
|
||||
app._compile_page("about")
|
||||
assert app.pages.keys() == {"index", "about"}
|
||||
assert app._pages.keys() == {"index", "about"}
|
||||
|
||||
|
||||
def test_add_page_set_route(app: App, index_page, windows_platform: bool):
|
||||
@ -255,10 +255,10 @@ def test_add_page_set_route(app: App, index_page, windows_platform: bool):
|
||||
windows_platform: Whether the system is windows.
|
||||
"""
|
||||
route = "test" if windows_platform else "/test"
|
||||
assert app.unevaluated_pages == {}
|
||||
assert app._unevaluated_pages == {}
|
||||
app.add_page(index_page, route=route)
|
||||
app._compile_page("test")
|
||||
assert app.pages.keys() == {"test"}
|
||||
assert app._pages.keys() == {"test"}
|
||||
|
||||
|
||||
def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
|
||||
@ -268,18 +268,18 @@ def test_add_page_set_route_dynamic(index_page, windows_platform: bool):
|
||||
index_page: The index page.
|
||||
windows_platform: Whether the system is windows.
|
||||
"""
|
||||
app = App(state=EmptyState)
|
||||
assert app.state is not None
|
||||
app = App(_state=EmptyState)
|
||||
assert app._state is not None
|
||||
route = "/test/[dynamic]"
|
||||
assert app.unevaluated_pages == {}
|
||||
assert app._unevaluated_pages == {}
|
||||
app.add_page(index_page, route=route)
|
||||
app._compile_page("test/[dynamic]")
|
||||
assert app.pages.keys() == {"test/[dynamic]"}
|
||||
assert "dynamic" in app.state.computed_vars
|
||||
assert app.state.computed_vars["dynamic"]._deps(objclass=EmptyState) == {
|
||||
assert app._pages.keys() == {"test/[dynamic]"}
|
||||
assert "dynamic" in app._state.computed_vars
|
||||
assert app._state.computed_vars["dynamic"]._deps(objclass=EmptyState) == {
|
||||
constants.ROUTER
|
||||
}
|
||||
assert constants.ROUTER in app.state()._computed_var_dependencies
|
||||
assert constants.ROUTER in app._state()._computed_var_dependencies
|
||||
|
||||
|
||||
def test_add_page_set_route_nested(app: App, index_page, windows_platform: bool):
|
||||
@ -291,9 +291,9 @@ def test_add_page_set_route_nested(app: App, index_page, windows_platform: bool)
|
||||
windows_platform: Whether the system is windows.
|
||||
"""
|
||||
route = "test\\nested" if windows_platform else "/test/nested"
|
||||
assert app.unevaluated_pages == {}
|
||||
assert app._unevaluated_pages == {}
|
||||
app.add_page(index_page, route=route)
|
||||
assert app.unevaluated_pages.keys() == {route.strip(os.path.sep)}
|
||||
assert app._unevaluated_pages.keys() == {route.strip(os.path.sep)}
|
||||
|
||||
|
||||
def test_add_page_invalid_api_route(app: App, index_page):
|
||||
@ -413,8 +413,8 @@ async def test_initialize_with_state(test_state: Type[ATestState], token: str):
|
||||
test_state: The default state.
|
||||
token: a Token.
|
||||
"""
|
||||
app = App(state=test_state)
|
||||
assert app.state == test_state
|
||||
app = App(_state=test_state)
|
||||
assert app._state == test_state
|
||||
|
||||
# Get a state for a given token.
|
||||
state = await app.state_manager.get_state(_substate_key(token, test_state))
|
||||
@ -432,7 +432,7 @@ async def test_set_and_get_state(test_state):
|
||||
Args:
|
||||
test_state: The default state.
|
||||
"""
|
||||
app = App(state=test_state)
|
||||
app = App(_state=test_state)
|
||||
|
||||
# Create two tokens.
|
||||
token1 = str(uuid.uuid4()) + f"_{test_state.get_full_name()}"
|
||||
@ -827,7 +827,7 @@ async def test_upload_file_without_annotation(state, tmp_path, token):
|
||||
token: a Token.
|
||||
"""
|
||||
state._tmp_path = tmp_path
|
||||
app = App(state=State)
|
||||
app = App(_state=State)
|
||||
|
||||
request_mock = unittest.mock.Mock()
|
||||
request_mock.headers = {
|
||||
@ -861,7 +861,7 @@ async def test_upload_file_background(state, tmp_path, token):
|
||||
token: a Token.
|
||||
"""
|
||||
state._tmp_path = tmp_path
|
||||
app = App(state=State)
|
||||
app = App(_state=State)
|
||||
|
||||
request_mock = unittest.mock.Mock()
|
||||
request_mock.headers = {
|
||||
@ -938,8 +938,8 @@ def test_dynamic_arg_shadow(
|
||||
"""
|
||||
arg_name = "counter"
|
||||
route = f"/test/[{arg_name}]"
|
||||
app = app_module_mock.app = App(state=DynamicState)
|
||||
assert app.state is not None
|
||||
app = app_module_mock.app = App(_state=DynamicState)
|
||||
assert app._state is not None
|
||||
with pytest.raises(NameError):
|
||||
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
|
||||
|
||||
@ -963,7 +963,7 @@ def test_multiple_dynamic_args(
|
||||
arg_name = "my_arg"
|
||||
route = f"/test/[{arg_name}]"
|
||||
route2 = f"/test2/[{arg_name}]"
|
||||
app = app_module_mock.app = App(state=EmptyState)
|
||||
app = app_module_mock.app = App(_state=EmptyState)
|
||||
app.add_page(index_page, route=route)
|
||||
app.add_page(index_page, route=route2)
|
||||
|
||||
@ -990,16 +990,16 @@ async def test_dynamic_route_var_route_change_completed_on_load(
|
||||
"""
|
||||
arg_name = "dynamic"
|
||||
route = f"/test/[{arg_name}]"
|
||||
app = app_module_mock.app = App(state=DynamicState)
|
||||
assert app.state is not None
|
||||
assert arg_name not in app.state.vars
|
||||
app = app_module_mock.app = App(_state=DynamicState)
|
||||
assert app._state is not None
|
||||
assert arg_name not in app._state.vars
|
||||
app.add_page(index_page, route=route, on_load=DynamicState.on_load) # type: ignore
|
||||
assert arg_name in app.state.vars
|
||||
assert arg_name in app.state.computed_vars
|
||||
assert app.state.computed_vars[arg_name]._deps(objclass=DynamicState) == {
|
||||
assert arg_name in app._state.vars
|
||||
assert arg_name in app._state.computed_vars
|
||||
assert app._state.computed_vars[arg_name]._deps(objclass=DynamicState) == {
|
||||
constants.ROUTER
|
||||
}
|
||||
assert constants.ROUTER in app.state()._computed_var_dependencies
|
||||
assert constants.ROUTER in app._state()._computed_var_dependencies
|
||||
|
||||
substate_token = _substate_key(token, DynamicState)
|
||||
sid = "mock_sid"
|
||||
@ -1174,7 +1174,7 @@ async def test_process_events(mocker, token: str):
|
||||
"headers": {},
|
||||
"ip": "127.0.0.1",
|
||||
}
|
||||
app = App(state=GenState)
|
||||
app = App(_state=GenState)
|
||||
|
||||
mocker.patch.object(app, "_postprocess", AsyncMock())
|
||||
event = Event(
|
||||
@ -1220,7 +1220,7 @@ def test_overlay_component(
|
||||
overlay_component: The overlay_component to pass to App.
|
||||
exp_page_child: The type of the expected child in the page fragment.
|
||||
"""
|
||||
app = App(state=state, overlay_component=overlay_component)
|
||||
app = App(_state=state, overlay_component=overlay_component)
|
||||
app._setup_overlay_component()
|
||||
if exp_page_child is None:
|
||||
assert app.overlay_component is None
|
||||
@ -1243,7 +1243,7 @@ def test_overlay_component(
|
||||
# overlay components are wrapped during compile only
|
||||
app._compile_page("test")
|
||||
app._setup_overlay_component()
|
||||
page = app.pages["test"]
|
||||
page = app._pages["test"]
|
||||
|
||||
if exp_page_child is not None:
|
||||
assert len(page.children) == 3
|
||||
@ -1361,52 +1361,52 @@ def test_app_wrap_priority(compilable_app: tuple[App, Path]):
|
||||
def test_app_state_determination():
|
||||
"""Test that the stateless status of an app is determined correctly."""
|
||||
a1 = App()
|
||||
assert a1.state is None
|
||||
assert a1._state is None
|
||||
|
||||
# No state, no router, no event handlers.
|
||||
a1.add_page(rx.box("Index"), route="/")
|
||||
assert a1.state is None
|
||||
assert a1._state is None
|
||||
|
||||
# Add a page with `on_load` enables state.
|
||||
a1.add_page(rx.box("About"), route="/about", on_load=rx.console_log(""))
|
||||
a1._compile_page("about")
|
||||
assert a1.state is not None
|
||||
assert a1._state is not None
|
||||
|
||||
a2 = App()
|
||||
assert a2.state is None
|
||||
assert a2._state is None
|
||||
|
||||
# Referencing a state Var enables state.
|
||||
a2.add_page(rx.box(rx.text(GenState.value)), route="/")
|
||||
a2._compile_page("index")
|
||||
assert a2.state is not None
|
||||
assert a2._state is not None
|
||||
|
||||
a3 = App()
|
||||
assert a3.state is None
|
||||
assert a3._state is None
|
||||
|
||||
# Referencing router enables state.
|
||||
a3.add_page(rx.box(rx.text(State.router.page.full_path)), route="/")
|
||||
a3._compile_page("index")
|
||||
assert a3.state is not None
|
||||
assert a3._state is not None
|
||||
|
||||
a4 = App()
|
||||
assert a4.state is None
|
||||
assert a4._state is None
|
||||
|
||||
a4.add_page(rx.box(rx.button("Click", on_click=rx.console_log(""))), route="/")
|
||||
assert a4.state is None
|
||||
assert a4._state is None
|
||||
|
||||
a4.add_page(
|
||||
rx.box(rx.button("Click", on_click=DynamicState.on_counter)), route="/page2"
|
||||
)
|
||||
a4._compile_page("page2")
|
||||
assert a4.state is not None
|
||||
assert a4._state is not None
|
||||
|
||||
|
||||
def test_raise_on_state():
|
||||
"""Test that the state is set."""
|
||||
# state kwargs is deprecated, we just make sure the app is created anyway.
|
||||
_app = App(state=State)
|
||||
assert _app.state is not None
|
||||
assert issubclass(_app.state, State)
|
||||
_app = App(_state=State)
|
||||
assert _app._state is not None
|
||||
assert issubclass(_app._state, State)
|
||||
|
||||
|
||||
def test_call_app():
|
||||
@ -1473,7 +1473,7 @@ def test_add_page_component_returning_tuple():
|
||||
app._compile_page("index")
|
||||
app._compile_page("page2")
|
||||
|
||||
fragment_wrapper = app.pages["index"].children[0]
|
||||
fragment_wrapper = app._pages["index"].children[0]
|
||||
assert isinstance(fragment_wrapper, Fragment)
|
||||
first_text = fragment_wrapper.children[0]
|
||||
assert isinstance(first_text, Text)
|
||||
@ -1483,7 +1483,7 @@ def test_add_page_component_returning_tuple():
|
||||
assert str(second_text.children[0].contents) == '"second"' # type: ignore
|
||||
|
||||
# Test page with trailing comma.
|
||||
page2_fragment_wrapper = app.pages["page2"].children[0]
|
||||
page2_fragment_wrapper = app._pages["page2"].children[0]
|
||||
assert isinstance(page2_fragment_wrapper, Fragment)
|
||||
third_text = page2_fragment_wrapper.children[0]
|
||||
assert isinstance(third_text, Text)
|
||||
@ -1557,7 +1557,7 @@ def test_app_with_valid_var_dependencies(compilable_app: tuple[App, Path]):
|
||||
def bar(self) -> str:
|
||||
return "bar"
|
||||
|
||||
app.state = ValidDepState
|
||||
app._state = ValidDepState
|
||||
app._compile()
|
||||
|
||||
|
||||
@ -1569,7 +1569,7 @@ def test_app_with_invalid_var_dependencies(compilable_app: tuple[App, Path]):
|
||||
def bar(self) -> str:
|
||||
return "bar"
|
||||
|
||||
app.state = InvalidDepState
|
||||
app._state = InvalidDepState
|
||||
with pytest.raises(exceptions.VarDependencyError):
|
||||
app._compile()
|
||||
|
||||
|
@ -1,20 +1,28 @@
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, mock_open
|
||||
|
||||
import pytest
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from reflex import constants
|
||||
from reflex.config import Config
|
||||
from reflex.reflex import cli
|
||||
from reflex.testing import chdir
|
||||
from reflex.utils.prerequisites import (
|
||||
CpuInfo,
|
||||
_update_next_config,
|
||||
cached_procedure,
|
||||
get_cpu_info,
|
||||
initialize_requirements_txt,
|
||||
rename_imports_and_app_name,
|
||||
)
|
||||
|
||||
runner = CliRunner()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config, export, expected_output",
|
||||
@ -224,3 +232,156 @@ def test_get_cpu_info():
|
||||
for attr in ("manufacturer_id", "model_name", "address_width"):
|
||||
value = getattr(cpu_info, attr)
|
||||
assert value.strip() if attr != "address_width" else value
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def temp_directory():
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
yield Path(temp_dir)
|
||||
shutil.rmtree(temp_dir)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"config_code,expected",
|
||||
[
|
||||
("rx.Config(app_name='old_name')", 'rx.Config(app_name="new_name")'),
|
||||
('rx.Config(app_name="old_name")', 'rx.Config(app_name="new_name")'),
|
||||
("rx.Config('old_name')", 'rx.Config("new_name")'),
|
||||
('rx.Config("old_name")', 'rx.Config("new_name")'),
|
||||
],
|
||||
)
|
||||
def test_rename_imports_and_app_name(temp_directory, config_code, expected):
|
||||
file_path = temp_directory / "rxconfig.py"
|
||||
content = f"""
|
||||
config = {config_code}
|
||||
"""
|
||||
file_path.write_text(content)
|
||||
|
||||
rename_imports_and_app_name(file_path, "old_name", "new_name")
|
||||
|
||||
updated_content = file_path.read_text()
|
||||
expected_content = f"""
|
||||
config = {expected}
|
||||
"""
|
||||
assert updated_content == expected_content
|
||||
|
||||
|
||||
def test_regex_edge_cases(temp_directory):
|
||||
file_path = temp_directory / "example.py"
|
||||
content = """
|
||||
from old_name.module import something
|
||||
import old_name
|
||||
from old_name import something_else as alias
|
||||
from old_name
|
||||
"""
|
||||
file_path.write_text(content)
|
||||
|
||||
rename_imports_and_app_name(file_path, "old_name", "new_name")
|
||||
|
||||
updated_content = file_path.read_text()
|
||||
expected_content = """
|
||||
from new_name.module import something
|
||||
import new_name
|
||||
from new_name import something_else as alias
|
||||
from new_name
|
||||
"""
|
||||
assert updated_content == expected_content
|
||||
|
||||
|
||||
def test_cli_rename_command(temp_directory):
|
||||
foo_dir = temp_directory / "foo"
|
||||
foo_dir.mkdir()
|
||||
(foo_dir / "__init__").touch()
|
||||
(foo_dir / ".web").mkdir()
|
||||
(foo_dir / "assets").mkdir()
|
||||
(foo_dir / "foo").mkdir()
|
||||
(foo_dir / "foo" / "__init__.py").touch()
|
||||
(foo_dir / "rxconfig.py").touch()
|
||||
(foo_dir / "rxconfig.py").write_text(
|
||||
"""
|
||||
import reflex as rx
|
||||
|
||||
config = rx.Config(
|
||||
app_name="foo",
|
||||
)
|
||||
"""
|
||||
)
|
||||
(foo_dir / "foo" / "components").mkdir()
|
||||
(foo_dir / "foo" / "components" / "__init__.py").touch()
|
||||
(foo_dir / "foo" / "components" / "base.py").touch()
|
||||
(foo_dir / "foo" / "components" / "views.py").touch()
|
||||
(foo_dir / "foo" / "components" / "base.py").write_text(
|
||||
"""
|
||||
import reflex as rx
|
||||
from foo.components import views
|
||||
from foo.components.views import *
|
||||
from .base import *
|
||||
|
||||
def random_component():
|
||||
return rx.fragment()
|
||||
"""
|
||||
)
|
||||
(foo_dir / "foo" / "foo.py").touch()
|
||||
(foo_dir / "foo" / "foo.py").write_text(
|
||||
"""
|
||||
import reflex as rx
|
||||
import foo.components.base
|
||||
from foo.components.base import random_component
|
||||
|
||||
class State(rx.State):
|
||||
pass
|
||||
|
||||
|
||||
def index():
|
||||
return rx.text("Hello, World!")
|
||||
|
||||
app = rx.App()
|
||||
app.add_page(index)
|
||||
"""
|
||||
)
|
||||
|
||||
with chdir(temp_directory / "foo"):
|
||||
result = runner.invoke(cli, ["rename", "bar"])
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert (foo_dir / "rxconfig.py").read_text() == (
|
||||
"""
|
||||
import reflex as rx
|
||||
|
||||
config = rx.Config(
|
||||
app_name="bar",
|
||||
)
|
||||
"""
|
||||
)
|
||||
assert (foo_dir / "bar").exists()
|
||||
assert not (foo_dir / "foo").exists()
|
||||
assert (foo_dir / "bar" / "components" / "base.py").read_text() == (
|
||||
"""
|
||||
import reflex as rx
|
||||
from bar.components import views
|
||||
from bar.components.views import *
|
||||
from .base import *
|
||||
|
||||
def random_component():
|
||||
return rx.fragment()
|
||||
"""
|
||||
)
|
||||
assert (foo_dir / "bar" / "bar.py").exists()
|
||||
assert not (foo_dir / "bar" / "foo.py").exists()
|
||||
assert (foo_dir / "bar" / "bar.py").read_text() == (
|
||||
"""
|
||||
import reflex as rx
|
||||
import bar.components.base
|
||||
from bar.components.base import random_component
|
||||
|
||||
class State(rx.State):
|
||||
pass
|
||||
|
||||
|
||||
def index():
|
||||
return rx.text("Hello, World!")
|
||||
|
||||
app = rx.App()
|
||||
app.add_page(index)
|
||||
"""
|
||||
)
|
||||
|
@ -89,7 +89,7 @@ def app():
|
||||
],
|
||||
)
|
||||
def test_check_routes_conflict_invalid(mocker, app, route1, route2):
|
||||
mocker.patch.object(app, "pages", {route1: []})
|
||||
mocker.patch.object(app, "_pages", {route1: []})
|
||||
with pytest.raises(ValueError):
|
||||
app._check_routes_conflict(route2)
|
||||
|
||||
@ -117,6 +117,6 @@ def test_check_routes_conflict_invalid(mocker, app, route1, route2):
|
||||
],
|
||||
)
|
||||
def test_check_routes_conflict_valid(mocker, app, route1, route2):
|
||||
mocker.patch.object(app, "pages", {route1: []})
|
||||
mocker.patch.object(app, "_pages", {route1: []})
|
||||
# test that running this does not throw an error.
|
||||
app._check_routes_conflict(route2)
|
||||
|
@ -1907,12 +1907,12 @@ def mock_app_simple(monkeypatch) -> rx.App:
|
||||
Returns:
|
||||
The app, after mocking out prerequisites.get_app()
|
||||
"""
|
||||
app = App(state=TestState)
|
||||
app = App(_state=TestState)
|
||||
|
||||
app_module = Mock()
|
||||
|
||||
setattr(app_module, CompileVars.APP, app)
|
||||
app.state = TestState
|
||||
app._state = TestState
|
||||
app.event_namespace.emit = CopyingAsyncMock() # type: ignore
|
||||
|
||||
def _mock_get_app(*args, **kwargs):
|
||||
@ -2153,7 +2153,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
|
||||
token: A token.
|
||||
"""
|
||||
router_data = {"query": {}}
|
||||
mock_app.state_manager.state = mock_app.state = BackgroundTaskState
|
||||
mock_app.state_manager.state = mock_app._state = BackgroundTaskState
|
||||
async for update in rx.app.process( # type: ignore
|
||||
mock_app,
|
||||
Event(
|
||||
@ -2171,7 +2171,7 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
|
||||
|
||||
# wait for the coroutine to start
|
||||
await asyncio.sleep(0.5 if CI else 0.1)
|
||||
assert len(mock_app.background_tasks) == 1
|
||||
assert len(mock_app._background_tasks) == 1
|
||||
|
||||
# Process another normal event
|
||||
async for update in rx.app.process( # type: ignore
|
||||
@ -2203,9 +2203,9 @@ async def test_background_task_no_block(mock_app: rx.App, token: str):
|
||||
)
|
||||
|
||||
# Explicit wait for background tasks
|
||||
for task in tuple(mock_app.background_tasks):
|
||||
for task in tuple(mock_app._background_tasks):
|
||||
await task
|
||||
assert not mock_app.background_tasks
|
||||
assert not mock_app._background_tasks
|
||||
|
||||
exp_order = [
|
||||
"background_task:start",
|
||||
@ -2280,7 +2280,7 @@ async def test_background_task_reset(mock_app: rx.App, token: str):
|
||||
token: A token.
|
||||
"""
|
||||
router_data = {"query": {}}
|
||||
mock_app.state_manager.state = mock_app.state = BackgroundTaskState
|
||||
mock_app.state_manager.state = mock_app._state = BackgroundTaskState
|
||||
async for update in rx.app.process( # type: ignore
|
||||
mock_app,
|
||||
Event(
|
||||
@ -2297,9 +2297,9 @@ async def test_background_task_reset(mock_app: rx.App, token: str):
|
||||
assert update == StateUpdate()
|
||||
|
||||
# Explicit wait for background tasks
|
||||
for task in tuple(mock_app.background_tasks):
|
||||
for task in tuple(mock_app._background_tasks):
|
||||
await task
|
||||
assert not mock_app.background_tasks
|
||||
assert not mock_app._background_tasks
|
||||
|
||||
assert (
|
||||
await mock_app.state_manager.get_state(
|
||||
@ -2884,7 +2884,7 @@ async def test_preprocess(app_module_mock, token, test_state, expected, mocker):
|
||||
"reflex.state.State.class_subclasses", {test_state, OnLoadInternalState}
|
||||
)
|
||||
app = app_module_mock.app = App(
|
||||
state=State, load_events={"index": [test_state.test_handler]}
|
||||
_state=State, _load_events={"index": [test_state.test_handler]}
|
||||
)
|
||||
async with app.state_manager.modify_state(_substate_key(token, State)) as state:
|
||||
state.router_data = {"simulate": "hydrate"}
|
||||
@ -2931,8 +2931,8 @@ async def test_preprocess_multiple_load_events(app_module_mock, token, mocker):
|
||||
"reflex.state.State.class_subclasses", {OnLoadState, OnLoadInternalState}
|
||||
)
|
||||
app = app_module_mock.app = App(
|
||||
state=State,
|
||||
load_events={"index": [OnLoadState.test_handler, OnLoadState.test_handler]},
|
||||
_state=State,
|
||||
_load_events={"index": [OnLoadState.test_handler, OnLoadState.test_handler]},
|
||||
)
|
||||
async with app.state_manager.modify_state(_substate_key(token, State)) as state:
|
||||
state.router_data = {"simulate": "hydrate"}
|
||||
@ -2977,7 +2977,7 @@ async def test_get_state(mock_app: rx.App, token: str):
|
||||
mock_app: An app that will be returned by `get_app()`
|
||||
token: A token.
|
||||
"""
|
||||
mock_app.state_manager.state = mock_app.state = TestState
|
||||
mock_app.state_manager.state = mock_app._state = TestState
|
||||
|
||||
# Get instance of ChildState2.
|
||||
test_state = await mock_app.state_manager.get_state(
|
||||
@ -3147,7 +3147,7 @@ async def test_get_state_from_sibling_not_cached(mock_app: rx.App, token: str):
|
||||
|
||||
pass
|
||||
|
||||
mock_app.state_manager.state = mock_app.state = Parent
|
||||
mock_app.state_manager.state = mock_app._state = Parent
|
||||
|
||||
# Get the top level state via unconnected sibling.
|
||||
root = await mock_app.state_manager.get_state(_substate_key(token, Child))
|
||||
|
@ -222,7 +222,7 @@ async def state_manager_redis(
|
||||
Yields:
|
||||
A state manager instance
|
||||
"""
|
||||
app_module_mock.app = rx.App(state=Root)
|
||||
app_module_mock.app = rx.App(_state=Root)
|
||||
state_manager = app_module_mock.app.state_manager
|
||||
|
||||
if not isinstance(state_manager, StateManagerRedis):
|
||||
|
@ -23,7 +23,7 @@ def test_app_harness(tmp_path):
|
||||
class State(rx.State):
|
||||
pass
|
||||
|
||||
app = rx.App(state=State)
|
||||
app = rx.App(_state=State)
|
||||
app.add_page(lambda: rx.text("Basic App"), route="/", title="index")
|
||||
app._compile()
|
||||
|
||||
|
@ -12,7 +12,7 @@ from reflex.base import Base
|
||||
from reflex.constants.base import REFLEX_VAR_CLOSING_TAG, REFLEX_VAR_OPENING_TAG
|
||||
from reflex.state import BaseState
|
||||
from reflex.utils.exceptions import (
|
||||
PrimitiveUnserializableToJSON,
|
||||
PrimitiveUnserializableToJSONError,
|
||||
UntypedComputedVarError,
|
||||
)
|
||||
from reflex.utils.imports import ImportVar
|
||||
@ -1061,7 +1061,7 @@ def test_inf_and_nan(var, expected_js):
|
||||
assert str(var) == expected_js
|
||||
assert isinstance(var, NumberVar)
|
||||
assert isinstance(var, LiteralVar)
|
||||
with pytest.raises(PrimitiveUnserializableToJSON):
|
||||
with pytest.raises(PrimitiveUnserializableToJSONError):
|
||||
var.json()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user