diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml
index 6da40ef6f..b794106e1 100644
--- a/.github/workflows/benchmarks.yml
+++ b/.github/workflows/benchmarks.yml
@@ -70,56 +70,6 @@ jobs:
env:
GITHUB_SHA: ${{ github.sha }}
- simple-apps-benchmarks: # This app tests the compile times of various compoonents and pages
- if: github.event.pull_request.merged == true
- env:
- OUTPUT_FILE: benchmarks.json
- timeout-minutes: 50
- strategy:
- # Prioritize getting more information out of the workflow (even if something fails)
- fail-fast: false
- 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"]
- exclude:
- - os: windows-latest
- 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"
- - os: macos-latest
- python-version: "3.11.11"
- include:
- - os: windows-latest
- python-version: "3.10.11"
- - os: windows-latest
- python-version: "3.11.9"
-
- runs-on: ${{ matrix.os }}
- steps:
- - uses: actions/checkout@v4
- - uses: ./.github/actions/setup_build_env
- with:
- python-version: ${{ matrix.python-version }}
- run-poetry-install: true
- create-venv-at-path: .venv
- - name: Run benchmark tests
- env:
- APP_HARNESS_HEADLESS: 1
- PYTHONUNBUFFERED: 1
- run: |
- poetry run pytest -v benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} -s
- - name: Upload benchmark results
- # Only run if the database creds are available in this context.
- run:
- poetry run python benchmarks/benchmark_compile_times.py --os "${{ matrix.os }}"
- --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
- --benchmark-json "${{ env.OUTPUT_FILE }}" --branch-name "${{ github.head_ref || github.ref_name }}"
- --event-type "${{ github.event_name }}" --pr-id "${{ github.event.pull_request.id }}"
-
reflex-dist-size: # This job is used to calculate the size of the Reflex distribution (wheel file)
if: github.event.pull_request.merged == true
timeout-minutes: 30
diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml
index b02604fd6..dc5a14d88 100644
--- a/.github/workflows/integration_tests.yml
+++ b/.github/workflows/integration_tests.yml
@@ -94,26 +94,6 @@ jobs:
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./reflex-examples/counter dev
- - name: Measure and upload .web size
- run:
- poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
- --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
- --pr-id "${{ github.event.pull_request.id }}"
- --branch-name "${{ github.head_ref || github.ref_name }}"
- --path ./reflex-examples/counter/.web
- --app-name "counter"
- - name: Install hyperfine
- run: cargo install hyperfine
- - name: Benchmark imports
- working-directory: ./reflex-examples/counter
- run: hyperfine --warmup 3 "export POETRY_VIRTUALENVS_PATH=../../.venv; poetry run python counter/counter.py" --show-output --export-json "${{ env.OUTPUT_FILE }}" --shell bash
- - name: Upload Benchmarks
- run:
- poetry run python benchmarks/benchmark_imports.py --os "${{ matrix.os }}"
- --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
- --benchmark-json "./reflex-examples/counter/${{ env.OUTPUT_FILE }}"
- --branch-name "${{ github.head_ref || github.ref_name }}" --pr-id "${{ github.event.pull_request.id }}"
- --app-name "counter"
- name: Install requirements for nba proxy example
working-directory: ./reflex-examples/nba-proxy
run: |
@@ -174,12 +154,6 @@ jobs:
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./reflex-web prod
- - name: Measure and upload .web size
- run:
- poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
- --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
- --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
- --app-name "reflex-web" --path ./reflex-web/.web
rx-shout-from-template:
strategy:
@@ -243,9 +217,3 @@ jobs:
# Check that npm is home
npm -v
poetry run bash scripts/integration.sh ./reflex-web prod
- - name: Measure and upload .web size
- run:
- poetry run python benchmarks/benchmark_web_size.py --os "${{ matrix.os }}"
- --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}"
- --pr-id "${{ github.event.pull_request.id }}" --branch-name "${{ github.head_ref || github.ref_name }}"
- --app-name "reflex-web" --path ./reflex-web/.web
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 0bad7b996..743f5f31a 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -3,7 +3,7 @@ fail_fast: true
repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit
- rev: v0.9.3
+ rev: v0.9.6
hooks:
- id: ruff-format
args: [reflex, tests]
diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py
deleted file mode 100644
index 9bcfbf85b..000000000
--- a/benchmarks/test_benchmark_compile_components.py
+++ /dev/null
@@ -1,376 +0,0 @@
-"""Benchmark tests for apps with varying component numbers."""
-
-from __future__ import annotations
-
-import functools
-import time
-from typing import Generator
-
-import pytest
-
-from benchmarks import WINDOWS_SKIP_REASON
-from reflex import constants
-from reflex.compiler import utils
-from reflex.testing import AppHarness, chdir
-from reflex.utils import build
-from reflex.utils.prerequisites import get_web_dir
-
-web_pages = get_web_dir() / constants.Dirs.PAGES
-
-
-def render_component(num: int):
- """Generate a number of components based on num.
-
- Args:
- num: number of components to produce.
-
- Returns:
- The rendered number of components.
- """
- import reflex as rx
-
- return [
- rx.fragment(
- rx.box(
- rx.accordion.root(
- rx.accordion.item(
- header="Full Ingredients",
- content="Yes. It's built with accessibility in mind.",
- font_size="3em",
- ),
- rx.accordion.item(
- header="Applications",
- content="Yes. It's unstyled by default, giving you freedom over the look and feel.",
- ),
- collapsible=True,
- variant="ghost",
- width="25rem",
- ),
- padding_top="20px",
- ),
- rx.box(
- rx.drawer.root(
- rx.drawer.trigger(
- rx.button("Open Drawer with snap points"), as_child=True
- ),
- rx.drawer.overlay(),
- rx.drawer.portal(
- rx.drawer.content(
- rx.flex(
- rx.drawer.title("Drawer Content"),
- rx.drawer.description("Drawer description"),
- rx.drawer.close(
- rx.button("Close Button"),
- as_child=True,
- ),
- direction="column",
- margin="5em",
- align_items="center",
- ),
- top="auto",
- height="100%",
- flex_direction="column",
- background_color="var(--green-3)",
- ),
- ),
- snap_points=["148px", "355px", 1],
- ),
- ),
- rx.box(
- rx.callout(
- "You will need admin privileges to install and access this application.",
- icon="info",
- size="3",
- ),
- ),
- rx.box(
- rx.table.root(
- rx.table.header(
- rx.table.row(
- rx.table.column_header_cell("Full name"),
- rx.table.column_header_cell("Email"),
- rx.table.column_header_cell("Group"),
- ),
- ),
- rx.table.body(
- rx.table.row(
- rx.table.row_header_cell("Danilo Sousa"),
- rx.table.cell("danilo@example.com"),
- rx.table.cell("Developer"),
- ),
- rx.table.row(
- rx.table.row_header_cell("Zahra Ambessa"),
- rx.table.cell("zahra@example.com"),
- rx.table.cell("Admin"),
- ),
- rx.table.row(
- rx.table.row_header_cell("Jasper Eriksson"),
- rx.table.cell("jasper@example.com"),
- rx.table.cell("Developer"),
- ),
- ),
- )
- ),
- )
- ] * num
-
-
-def AppWithTenComponentsOnePage():
- """A reflex app with roughly 10 components on one page."""
- import reflex as rx
-
- def index() -> rx.Component:
- return rx.center(rx.vstack(*render_component(1)))
-
- app = rx.App(_state=rx.State)
- app.add_page(index)
-
-
-def AppWithHundredComponentOnePage():
- """A reflex app with roughly 100 components on one page."""
- import reflex as rx
-
- def index() -> rx.Component:
- return rx.center(rx.vstack(*render_component(100)))
-
- app = rx.App(_state=rx.State)
- app.add_page(index)
-
-
-def AppWithThousandComponentsOnePage():
- """A reflex app with roughly 1000 components on one page."""
- import reflex as rx
-
- def index() -> rx.Component:
- return rx.center(rx.vstack(*render_component(1000)))
-
- app = rx.App(_state=rx.State)
- app.add_page(index)
-
-
-@pytest.fixture(scope="session")
-def app_with_10_components(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Start Blank Template app at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- running AppHarness instance
- """
- root = tmp_path_factory.mktemp("app10components")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithTenComponentsOnePage,
- render_component=render_component, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.fixture(scope="session")
-def app_with_100_components(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Start Blank Template app at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- running AppHarness instance
- """
- root = tmp_path_factory.mktemp("app100components")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithHundredComponentOnePage,
- render_component=render_component, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.fixture(scope="session")
-def app_with_1000_components(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 1000 components at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- an AppHarness instance
- """
- root = tmp_path_factory.mktemp("app1000components")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithThousandComponentsOnePage,
- render_component=render_component, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10_compile_time_cold(benchmark, app_with_10_components):
- """Test the compile time on a cold start for an app with roughly 10 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_10_components: The app harness.
- """
-
- def setup():
- with chdir(app_with_10_components.app_path):
- utils.empty_dir(web_pages, ["_app.js"])
- app_with_10_components._initialize_app()
- build.setup_frontend(app_with_10_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_10_components.app_path):
- app_with_10_components.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=10)
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10_compile_time_warm(benchmark, app_with_10_components):
- """Test the compile time on a warm start for an app with roughly 10 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_10_components: The app harness.
- """
- with chdir(app_with_10_components.app_path):
- app_with_10_components._initialize_app()
- build.setup_frontend(app_with_10_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_10_components.app_path):
- app_with_10_components.app_instance._compile()
-
- benchmark(benchmark_fn)
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_100_compile_time_cold(benchmark, app_with_100_components):
- """Test the compile time on a cold start for an app with roughly 100 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_100_components: The app harness.
- """
-
- def setup():
- with chdir(app_with_100_components.app_path):
- utils.empty_dir(web_pages, ["_app.js"])
- app_with_100_components._initialize_app()
- build.setup_frontend(app_with_100_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_100_components.app_path):
- app_with_100_components.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_100_compile_time_warm(benchmark, app_with_100_components):
- """Test the compile time on a warm start for an app with roughly 100 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_100_components: The app harness.
- """
- with chdir(app_with_100_components.app_path):
- app_with_100_components._initialize_app()
- build.setup_frontend(app_with_100_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_100_components.app_path):
- app_with_100_components.app_instance._compile()
-
- benchmark(benchmark_fn)
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1000_compile_time_cold(benchmark, app_with_1000_components):
- """Test the compile time on a cold start for an app with roughly 1000 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_1000_components: The app harness.
- """
-
- def setup():
- with chdir(app_with_1000_components.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_1000_components._initialize_app()
- build.setup_frontend(app_with_1000_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_1000_components.app_path):
- app_with_1000_components.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying component numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1000_compile_time_warm(benchmark, app_with_1000_components):
- """Test the compile time on a warm start for an app with roughly 1000 components.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_1000_components: The app harness.
- """
- with chdir(app_with_1000_components.app_path):
- app_with_1000_components._initialize_app()
- build.setup_frontend(app_with_1000_components.app_path)
-
- def benchmark_fn():
- with chdir(app_with_1000_components.app_path):
- app_with_1000_components.app_instance._compile()
-
- benchmark(benchmark_fn)
diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py
deleted file mode 100644
index 149fc6130..000000000
--- a/benchmarks/test_benchmark_compile_pages.py
+++ /dev/null
@@ -1,579 +0,0 @@
-"""Benchmark tests for apps with varying page numbers."""
-
-from __future__ import annotations
-
-import functools
-import time
-from typing import Generator
-
-import pytest
-
-from benchmarks import WINDOWS_SKIP_REASON
-from reflex import constants
-from reflex.compiler import utils
-from reflex.testing import AppHarness, chdir
-from reflex.utils import build
-from reflex.utils.prerequisites import get_web_dir
-
-web_pages = get_web_dir() / constants.Dirs.PAGES
-
-
-def render_multiple_pages(app, num: int):
- """Add multiple pages based on num.
-
- Args:
- app: The App object.
- num: number of pages to render.
-
- """
- from typing import Tuple
-
- from rxconfig import config # pyright: ignore [reportMissingImports]
-
- import reflex as rx
-
- docs_url = "https://reflex.dev/docs/getting-started/introduction/"
- filename = f"{config.app_name}/{config.app_name}.py"
- college = [
- "Stanford University",
- "Arizona",
- "Arizona state",
- "Baylor",
- "Boston College",
- "Boston University",
- ]
-
- class State(rx.State):
- """The app state."""
-
- position: str
- college: str
- age: Tuple[int, int] = (18, 50)
- salary: Tuple[int, int] = (0, 25000000)
-
- comp1 = rx.center(
- rx.theme_panel(),
- rx.vstack(
- rx.heading("Welcome to Reflex!", size="9"),
- rx.text("Get started by editing ", rx.code(filename)),
- rx.button(
- "Check out our docs!",
- on_click=lambda: rx.redirect(docs_url),
- size="4",
- ),
- align="center",
- spacing="7",
- font_size="2em",
- ),
- height="100vh",
- )
-
- comp2 = rx.vstack(
- rx.hstack(
- rx.vstack(
- rx.select(
- ["C", "PF", "SF", "PG", "SG"],
- placeholder="Select a position. (All)",
- on_change=State.set_position, # pyright: ignore [reportAttributeAccessIssue]
- size="3",
- ),
- rx.select(
- college,
- placeholder="Select a college. (All)",
- on_change=State.set_college, # pyright: ignore [reportAttributeAccessIssue]
- size="3",
- ),
- ),
- rx.vstack(
- rx.vstack(
- rx.hstack(
- rx.badge("Min Age: ", State.age[0]),
- rx.divider(orientation="vertical"),
- rx.badge("Max Age: ", State.age[1]),
- ),
- rx.slider(
- default_value=[18, 50],
- min=18,
- max=50,
- on_value_commit=State.set_age, # pyright: ignore [reportAttributeAccessIssue]
- ),
- align_items="left",
- width="100%",
- ),
- rx.vstack(
- rx.hstack(
- rx.badge("Min Sal: ", State.salary[0] // 1000000, "M"),
- rx.divider(orientation="vertical"),
- rx.badge("Max Sal: ", State.salary[1] // 1000000, "M"),
- ),
- rx.slider(
- default_value=[0, 25000000],
- min=0,
- max=25000000,
- on_value_commit=State.set_salary, # pyright: ignore [reportAttributeAccessIssue]
- ),
- align_items="left",
- width="100%",
- ),
- ),
- spacing="4",
- ),
- width="100%",
- )
-
- for i in range(1, num + 1):
- if i % 2 == 1:
- app.add_page(comp1, route=f"page{i}")
- else:
- app.add_page(comp2, route=f"page{i}")
-
-
-def AppWithOnePage():
- """A reflex app with one page."""
- from rxconfig import config # pyright: ignore [reportMissingImports]
-
- import reflex as rx
-
- docs_url = "https://reflex.dev/docs/getting-started/introduction/"
- filename = f"{config.app_name}/{config.app_name}.py"
-
- class State(rx.State):
- """The app state."""
-
- pass
-
- def index() -> rx.Component:
- return rx.center(
- rx.input(
- id="token", value=State.router.session.client_token, is_read_only=True
- ),
- rx.vstack(
- rx.heading("Welcome to Reflex!", size="9"),
- rx.text("Get started by editing ", rx.code(filename)),
- rx.button(
- "Check out our docs!",
- on_click=lambda: rx.redirect(docs_url),
- size="4",
- ),
- align="center",
- spacing="7",
- font_size="2em",
- ),
- height="100vh",
- )
-
- app = rx.App(_state=rx.State)
- app.add_page(index)
-
-
-def AppWithTenPages():
- """A reflex app with 10 pages."""
- import reflex as rx
-
- app = rx.App(_state=rx.State)
- render_multiple_pages(app, 10)
-
-
-def AppWithHundredPages():
- """A reflex app with 100 pages."""
- import reflex as rx
-
- app = rx.App(_state=rx.State)
- render_multiple_pages(app, 100)
-
-
-def AppWithThousandPages():
- """A reflex app with Thousand pages."""
- import reflex as rx
-
- app = rx.App(_state=rx.State)
- render_multiple_pages(app, 1000)
-
-
-def AppWithTenThousandPages():
- """A reflex app with ten thousand pages."""
- import reflex as rx
-
- app = rx.App(_state=rx.State)
- render_multiple_pages(app, 10000)
-
-
-@pytest.fixture(scope="session")
-def app_with_one_page(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 10000 pages at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- an AppHarness instance
- """
- root = tmp_path_factory.mktemp("app1")
-
- yield AppHarness.create(root=root, app_source=AppWithOnePage)
-
-
-@pytest.fixture(scope="session")
-def app_with_ten_pages(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 10 pages at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- an AppHarness instance
- """
- root = tmp_path_factory.mktemp("app10")
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithTenPages,
- render_comp=render_multiple_pages, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.fixture(scope="session")
-def app_with_hundred_pages(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 100 pages at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- an AppHarness instance
- """
- root = tmp_path_factory.mktemp("app100")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithHundredPages,
- render_comp=render_multiple_pages, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.fixture(scope="session")
-def app_with_thousand_pages(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 1000 pages at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- an AppHarness instance
- """
- root = tmp_path_factory.mktemp("app1000")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithThousandPages,
- render_comp=render_multiple_pages, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.fixture(scope="session")
-def app_with_ten_thousand_pages(
- tmp_path_factory,
-) -> Generator[AppHarness, None, None]:
- """Create an app with 10000 pages at tmp_path via AppHarness.
-
- Args:
- tmp_path_factory: pytest tmp_path_factory fixture
-
- Yields:
- running AppHarness instance
- """
- root = tmp_path_factory.mktemp("app10000")
-
- yield AppHarness.create(
- root=root,
- app_source=functools.partial(
- AppWithTenThousandPages,
- render_comp=render_multiple_pages, # pyright: ignore [reportCallIssue]
- ),
- )
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1_compile_time_cold(benchmark, app_with_one_page):
- """Test the compile time on a cold start for an app with 1 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_one_page: The app harness.
- """
-
- def setup():
- with chdir(app_with_one_page.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_one_page._initialize_app()
- build.setup_frontend(app_with_one_page.app_path)
-
- def benchmark_fn():
- with chdir(app_with_one_page.app_path):
- app_with_one_page.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
- app_with_one_page._reload_state_module()
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1_compile_time_warm(benchmark, app_with_one_page):
- """Test the compile time on a warm start for an app with 1 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_one_page: The app harness.
- """
- with chdir(app_with_one_page.app_path):
- app_with_one_page._initialize_app()
- build.setup_frontend(app_with_one_page.app_path)
-
- def benchmark_fn():
- with chdir(app_with_one_page.app_path):
- app_with_one_page.app_instance._compile()
-
- benchmark(benchmark_fn)
- app_with_one_page._reload_state_module()
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10_compile_time_cold(benchmark, app_with_ten_pages):
- """Test the compile time on a cold start for an app with 10 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_ten_pages: The app harness.
- """
-
- def setup():
- with chdir(app_with_ten_pages.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_ten_pages._initialize_app()
- build.setup_frontend(app_with_ten_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_ten_pages.app_path):
- app_with_ten_pages.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
- app_with_ten_pages._reload_state_module()
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10_compile_time_warm(benchmark, app_with_ten_pages):
- """Test the compile time on a warm start for an app with 10 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_ten_pages: The app harness.
- """
- with chdir(app_with_ten_pages.app_path):
- app_with_ten_pages._initialize_app()
- build.setup_frontend(app_with_ten_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_ten_pages.app_path):
- app_with_ten_pages.app_instance._compile()
-
- benchmark(benchmark_fn)
- app_with_ten_pages._reload_state_module()
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_100_compile_time_cold(benchmark, app_with_hundred_pages):
- """Test the compile time on a cold start for an app with 100 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_hundred_pages: The app harness.
- """
-
- def setup():
- with chdir(app_with_hundred_pages.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_hundred_pages._initialize_app()
- build.setup_frontend(app_with_hundred_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_hundred_pages.app_path):
- app_with_hundred_pages.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
- app_with_hundred_pages._reload_state_module()
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_100_compile_time_warm(benchmark, app_with_hundred_pages):
- """Test the compile time on a warm start for an app with 100 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_hundred_pages: The app harness.
- """
- with chdir(app_with_hundred_pages.app_path):
- app_with_hundred_pages._initialize_app()
- build.setup_frontend(app_with_hundred_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_hundred_pages.app_path):
- app_with_hundred_pages.app_instance._compile()
-
- benchmark(benchmark_fn)
- app_with_hundred_pages._reload_state_module()
-
-
-@pytest.mark.skipif(constants.IS_WINDOWS, reason=WINDOWS_SKIP_REASON)
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1000_compile_time_cold(benchmark, app_with_thousand_pages):
- """Test the compile time on a cold start for an app with 1000 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_thousand_pages: The app harness.
- """
-
- def setup():
- with chdir(app_with_thousand_pages.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_thousand_pages._initialize_app()
- build.setup_frontend(app_with_thousand_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_thousand_pages.app_path):
- app_with_thousand_pages.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
- app_with_thousand_pages._reload_state_module()
-
-
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_1000_compile_time_warm(benchmark, app_with_thousand_pages):
- """Test the compile time on a warm start for an app with 1000 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_thousand_pages: The app harness.
- """
- with chdir(app_with_thousand_pages.app_path):
- app_with_thousand_pages._initialize_app()
- build.setup_frontend(app_with_thousand_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_thousand_pages.app_path):
- app_with_thousand_pages.app_instance._compile()
-
- benchmark(benchmark_fn)
- app_with_thousand_pages._reload_state_module()
-
-
-@pytest.mark.skip
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10000_compile_time_cold(benchmark, app_with_ten_thousand_pages):
- """Test the compile time on a cold start for an app with 10000 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_ten_thousand_pages: The app harness.
- """
-
- def setup():
- with chdir(app_with_ten_thousand_pages.app_path):
- utils.empty_dir(web_pages, keep_files=["_app.js"])
- app_with_ten_thousand_pages._initialize_app()
- build.setup_frontend(app_with_ten_thousand_pages.app_path)
-
- def benchmark_fn():
- with chdir(app_with_ten_thousand_pages.app_path):
- app_with_ten_thousand_pages.app_instance._compile()
-
- benchmark.pedantic(benchmark_fn, setup=setup, rounds=5)
- app_with_ten_thousand_pages._reload_state_module()
-
-
-@pytest.mark.skip
-@pytest.mark.benchmark(
- group="Compile time of varying page numbers",
- min_rounds=5,
- timer=time.perf_counter,
- disable_gc=True,
- warmup=False,
-)
-def test_app_10000_compile_time_warm(benchmark, app_with_ten_thousand_pages):
- """Test the compile time on a warm start for an app with 10000 page.
-
- Args:
- benchmark: The benchmark fixture.
- app_with_ten_thousand_pages: The app harness.
- """
-
- def benchmark_fn():
- with chdir(app_with_ten_thousand_pages.app_path):
- app_with_ten_thousand_pages.app_instance._compile()
-
- benchmark(benchmark_fn)
- app_with_ten_thousand_pages._reload_state_module()
diff --git a/poetry.lock b/poetry.lock
index b96749316..139ef29e0 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -112,7 +112,7 @@ description = "Backport of CPython tarfile module"
optional = false
python-versions = ">=3.8"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and python_version <= \"3.11\""
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34"},
{file = "backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991"},
@@ -251,7 +251,7 @@ files = [
{file = "cffi-1.17.1-cp39-cp39-win_amd64.whl", hash = "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662"},
{file = "cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824"},
]
-markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
+markers = {main = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
[package.dependencies]
pycparser = "*"
@@ -399,7 +399,7 @@ files = [
{file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"},
{file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"},
]
-markers = {main = "(platform_system == \"Windows\" or os_name == \"nt\") and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "(python_version <= \"3.11\" or python_version >= \"3.12\") and sys_platform == \"win32\""}
+markers = {main = "python_version <= \"3.11\" and platform_system == \"Windows\" or python_version <= \"3.11\" and os_name == \"nt\" or python_version >= \"3.12\" and platform_system == \"Windows\" or python_version >= \"3.12\" and os_name == \"nt\"", dev = "python_version <= \"3.11\" and sys_platform == \"win32\" or python_version >= \"3.12\" and sys_platform == \"win32\""}
[[package]]
name = "coverage"
@@ -488,7 +488,7 @@ description = "cryptography is a package which provides cryptographic recipes an
optional = false
python-versions = "!=3.9.0,!=3.9.1,>=3.7"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\""
files = [
{file = "cryptography-44.0.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:bf688f615c29bfe9dfc44312ca470989279f0e94bb9f631f85e3459af8efc009"},
{file = "cryptography-44.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd7c7e2d71d908dc0f8d2027e1604102140d84b155e658c20e8ad1304317691f"},
@@ -586,7 +586,7 @@ description = "Distro - an OS platform information API"
optional = false
python-versions = ">=3.6"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and sys_platform == \"linux\""
+markers = "python_version <= \"3.11\" and sys_platform == \"linux\" or python_version >= \"3.12\" and sys_platform == \"linux\""
files = [
{file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"},
{file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"},
@@ -743,7 +743,7 @@ files = [
{file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"},
{file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"},
]
-markers = {main = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") and python_version < \"3.14\"", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
+markers = {main = "python_version <= \"3.11\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\") or python_version >= \"3.12\" and python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
[package.extras]
docs = ["Sphinx", "furo"]
@@ -894,7 +894,7 @@ description = "Read metadata from Python packages"
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and python_version <= \"3.11\" or python_full_version < \"3.10.2\""
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_full_version < \"3.10.2\""
files = [
{file = "importlib_metadata-8.6.1-py3-none-any.whl", hash = "sha256:02a89390c1e15fdfdc0d7c6b25cb3e62650d0494005c97d6f148bf5b9787525e"},
{file = "importlib_metadata-8.6.1.tar.gz", hash = "sha256:310b41d755445d74569f993ccfc22838295d9fe005425094fad953d7f15c8580"},
@@ -932,7 +932,7 @@ description = "Utility functions for Python class constructs"
optional = false
python-versions = ">=3.8"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790"},
{file = "jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd"},
@@ -952,7 +952,7 @@ description = "Useful decorators and context managers"
optional = false
python-versions = ">=3.8"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "jaraco.context-6.0.1-py3-none-any.whl", hash = "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4"},
{file = "jaraco_context-6.0.1.tar.gz", hash = "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3"},
@@ -972,7 +972,7 @@ description = "Functools like those found in stdlib"
optional = false
python-versions = ">=3.8"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "jaraco.functools-4.1.0-py3-none-any.whl", hash = "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649"},
{file = "jaraco_functools-4.1.0.tar.gz", hash = "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d"},
@@ -996,7 +996,7 @@ description = "Low-level, pure Python DBus protocol wrapper."
optional = false
python-versions = ">=3.7"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\""
files = [
{file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"},
{file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"},
@@ -1032,7 +1032,7 @@ description = "Store and access your passwords safely."
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "keyring-25.6.0-py3-none-any.whl", hash = "sha256:552a3f7af126ece7ed5c89753650eec89c7eaae8617d0aa4d9ad2b75111266bd"},
{file = "keyring-25.6.0.tar.gz", hash = "sha256:0b39998aa941431eb3d9b0d4b2460bc773b9df6fed7621c2dfb291a7e0187a66"},
@@ -1216,7 +1216,7 @@ description = "More routines for operating on iterables, beyond itertools"
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "(python_version <= \"3.11\" or python_version >= \"3.12\") and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\")"
files = [
{file = "more-itertools-10.6.0.tar.gz", hash = "sha256:2cd7fad1009c31cc9fb6a035108509e6547547a7a738374f10bd49a09eb3ee3b"},
{file = "more_itertools-10.6.0-py3-none-any.whl", hash = "sha256:6eb054cb4b6db1473f6e15fcc676a08e4732548acd47c708f0e179c2c7c01e89"},
@@ -1272,68 +1272,68 @@ files = [
[[package]]
name = "numpy"
-version = "2.2.2"
+version = "2.2.3"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = ">=3.10"
groups = ["dev"]
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
files = [
- {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"},
+ {file = "numpy-2.2.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cbc6472e01952d3d1b2772b720428f8b90e2deea8344e854df22b0618e9cce71"},
+ {file = "numpy-2.2.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdfe0c22692a30cd830c0755746473ae66c4a8f2e7bd508b35fb3b6a0813d787"},
+ {file = "numpy-2.2.3-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:e37242f5324ffd9f7ba5acf96d774f9276aa62a966c0bad8dae692deebec7716"},
+ {file = "numpy-2.2.3-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:95172a21038c9b423e68be78fd0be6e1b97674cde269b76fe269a5dfa6fadf0b"},
+ {file = "numpy-2.2.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5b47c440210c5d1d67e1cf434124e0b5c395eee1f5806fdd89b553ed1acd0a3"},
+ {file = "numpy-2.2.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0391ea3622f5c51a2e29708877d56e3d276827ac5447d7f45e9bc4ade8923c52"},
+ {file = "numpy-2.2.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f6b3dfc7661f8842babd8ea07e9897fe3d9b69a1d7e5fbb743e4160f9387833b"},
+ {file = "numpy-2.2.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1ad78ce7f18ce4e7df1b2ea4019b5817a2f6a8a16e34ff2775f646adce0a5027"},
+ {file = "numpy-2.2.3-cp310-cp310-win32.whl", hash = "sha256:5ebeb7ef54a7be11044c33a17b2624abe4307a75893c001a4800857956b41094"},
+ {file = "numpy-2.2.3-cp310-cp310-win_amd64.whl", hash = "sha256:596140185c7fa113563c67c2e894eabe0daea18cf8e33851738c19f70ce86aeb"},
+ {file = "numpy-2.2.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:16372619ee728ed67a2a606a614f56d3eabc5b86f8b615c79d01957062826ca8"},
+ {file = "numpy-2.2.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5521a06a3148686d9269c53b09f7d399a5725c47bbb5b35747e1cb76326b714b"},
+ {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:7c8dde0ca2f77828815fd1aedfdf52e59071a5bae30dac3b4da2a335c672149a"},
+ {file = "numpy-2.2.3-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:77974aba6c1bc26e3c205c2214f0d5b4305bdc719268b93e768ddb17e3fdd636"},
+ {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d42f9c36d06440e34226e8bd65ff065ca0963aeecada587b937011efa02cdc9d"},
+ {file = "numpy-2.2.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2712c5179f40af9ddc8f6727f2bd910ea0eb50206daea75f58ddd9fa3f715bb"},
+ {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c8b0451d2ec95010d1db8ca733afc41f659f425b7f608af569711097fd6014e2"},
+ {file = "numpy-2.2.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d9b4a8148c57ecac25a16b0e11798cbe88edf5237b0df99973687dd866f05e1b"},
+ {file = "numpy-2.2.3-cp311-cp311-win32.whl", hash = "sha256:1f45315b2dc58d8a3e7754fe4e38b6fce132dab284a92851e41b2b344f6441c5"},
+ {file = "numpy-2.2.3-cp311-cp311-win_amd64.whl", hash = "sha256:9f48ba6f6c13e5e49f3d3efb1b51c8193215c42ac82610a04624906a9270be6f"},
+ {file = "numpy-2.2.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:12c045f43b1d2915eca6b880a7f4a256f59d62df4f044788c8ba67709412128d"},
+ {file = "numpy-2.2.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:87eed225fd415bbae787f93a457af7f5990b92a334e346f72070bf569b9c9c95"},
+ {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:712a64103d97c404e87d4d7c47fb0c7ff9acccc625ca2002848e0d53288b90ea"},
+ {file = "numpy-2.2.3-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a5ae282abe60a2db0fd407072aff4599c279bcd6e9a2475500fc35b00a57c532"},
+ {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5266de33d4c3420973cf9ae3b98b54a2a6d53a559310e3236c4b2b06b9c07d4e"},
+ {file = "numpy-2.2.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b787adbf04b0db1967798dba8da1af07e387908ed1553a0d6e74c084d1ceafe"},
+ {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:34c1b7e83f94f3b564b35f480f5652a47007dd91f7c839f404d03279cc8dd021"},
+ {file = "numpy-2.2.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4d8335b5f1b6e2bce120d55fb17064b0262ff29b459e8493d1785c18ae2553b8"},
+ {file = "numpy-2.2.3-cp312-cp312-win32.whl", hash = "sha256:4d9828d25fb246bedd31e04c9e75714a4087211ac348cb39c8c5f99dbb6683fe"},
+ {file = "numpy-2.2.3-cp312-cp312-win_amd64.whl", hash = "sha256:83807d445817326b4bcdaaaf8e8e9f1753da04341eceec705c001ff342002e5d"},
+ {file = "numpy-2.2.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7bfdb06b395385ea9b91bf55c1adf1b297c9fdb531552845ff1d3ea6e40d5aba"},
+ {file = "numpy-2.2.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:23c9f4edbf4c065fddb10a4f6e8b6a244342d95966a48820c614891e5059bb50"},
+ {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c03b6be48aaf92525cccf393265e02773be8fd9551a2f9adbe7db1fa2b60f1"},
+ {file = "numpy-2.2.3-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:2376e317111daa0a6739e50f7ee2a6353f768489102308b0d98fcf4a04f7f3b5"},
+ {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8fb62fe3d206d72fe1cfe31c4a1106ad2b136fcc1606093aeab314f02930fdf2"},
+ {file = "numpy-2.2.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52659ad2534427dffcc36aac76bebdd02b67e3b7a619ac67543bc9bfe6b7cdb1"},
+ {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1b416af7d0ed3271cad0f0a0d0bee0911ed7eba23e66f8424d9f3dfcdcae1304"},
+ {file = "numpy-2.2.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:1402da8e0f435991983d0a9708b779f95a8c98c6b18a171b9f1be09005e64d9d"},
+ {file = "numpy-2.2.3-cp313-cp313-win32.whl", hash = "sha256:136553f123ee2951bfcfbc264acd34a2fc2f29d7cdf610ce7daf672b6fbaa693"},
+ {file = "numpy-2.2.3-cp313-cp313-win_amd64.whl", hash = "sha256:5b732c8beef1d7bc2d9e476dbba20aaff6167bf205ad9aa8d30913859e82884b"},
+ {file = "numpy-2.2.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:435e7a933b9fda8126130b046975a968cc2d833b505475e588339e09f7672890"},
+ {file = "numpy-2.2.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7678556eeb0152cbd1522b684dcd215250885993dd00adb93679ec3c0e6e091c"},
+ {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:2e8da03bd561504d9b20e7a12340870dfc206c64ea59b4cfee9fceb95070ee94"},
+ {file = "numpy-2.2.3-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:c9aa4496fd0e17e3843399f533d62857cef5900facf93e735ef65aa4bbc90ef0"},
+ {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4ca91d61a4bf61b0f2228f24bbfa6a9facd5f8af03759fe2a655c50ae2c6610"},
+ {file = "numpy-2.2.3-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:deaa09cd492e24fd9b15296844c0ad1b3c976da7907e1c1ed3a0ad21dded6f76"},
+ {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:246535e2f7496b7ac85deffe932896a3577be7af8fb7eebe7146444680297e9a"},
+ {file = "numpy-2.2.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:daf43a3d1ea699402c5a850e5313680ac355b4adc9770cd5cfc2940e7861f1bf"},
+ {file = "numpy-2.2.3-cp313-cp313t-win32.whl", hash = "sha256:cf802eef1f0134afb81fef94020351be4fe1d6681aadf9c5e862af6602af64ef"},
+ {file = "numpy-2.2.3-cp313-cp313t-win_amd64.whl", hash = "sha256:aee2512827ceb6d7f517c8b85aa5d3923afe8fc7a57d028cffcd522f1c6fd082"},
+ {file = "numpy-2.2.3-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3c2ec8a0f51d60f1e9c0c5ab116b7fc104b165ada3f6c58abf881cb2eb16044d"},
+ {file = "numpy-2.2.3-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ed2cf9ed4e8ebc3b754d398cba12f24359f018b416c380f577bbae112ca52fc9"},
+ {file = "numpy-2.2.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39261798d208c3095ae4f7bc8eaeb3481ea8c6e03dc48028057d3cbdbdb8937e"},
+ {file = "numpy-2.2.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:783145835458e60fa97afac25d511d00a1eca94d4a8f3ace9fe2043003c678e4"},
+ {file = "numpy-2.2.3.tar.gz", hash = "sha256:dbdc15f0c81611925f382dfa97b3bd0bc2c1ce19d4fe50482cb0ddc12ba30020"},
]
[[package]]
@@ -1420,9 +1420,9 @@ files = [
[package.dependencies]
numpy = [
+ {version = ">=1.22.4", markers = "python_version < \"3.11\""},
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
- {version = ">=1.22.4", markers = "python_version < \"3.11\""},
]
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
@@ -1693,7 +1693,7 @@ files = [
{file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"},
{file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"},
]
-markers = {main = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" and (python_version <= \"3.11\" or python_version >= \"3.12\")", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
+markers = {main = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and platform_python_implementation != \"PyPy\"", dev = "python_version <= \"3.11\" or python_version >= \"3.12\""}
[[package]]
name = "pydantic"
@@ -1881,15 +1881,15 @@ files = [
[[package]]
name = "pyright"
-version = "1.1.393"
+version = "1.1.394"
description = "Command line wrapper for pyright"
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "pyright-1.1.393-py3-none-any.whl", hash = "sha256:8320629bb7a44ca90944ba599390162bf59307f3d9fb6e27da3b7011b8c17ae5"},
- {file = "pyright-1.1.393.tar.gz", hash = "sha256:aeeb7ff4e0364775ef416a80111613f91a05c8e01e58ecfefc370ca0db7aed9c"},
+ {file = "pyright-1.1.394-py3-none-any.whl", hash = "sha256:5f74cce0a795a295fb768759bbeeec62561215dea657edcaab48a932b031ddbb"},
+ {file = "pyright-1.1.394.tar.gz", hash = "sha256:56f2a3ab88c5214a451eb71d8f2792b7700434f841ea219119ade7f42ca93608"},
]
[package.dependencies]
@@ -2203,7 +2203,7 @@ description = "A (partial) reimplementation of pywin32 using ctypes/cffi"
optional = false
python-versions = ">=3.6"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"win32\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"win32\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"win32\""
files = [
{file = "pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755"},
{file = "pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8"},
@@ -2415,31 +2415,31 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"]
[[package]]
name = "ruff"
-version = "0.9.3"
+version = "0.9.6"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
groups = ["dev"]
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "ruff-0.9.3-py3-none-linux_armv6l.whl", hash = "sha256:7f39b879064c7d9670197d91124a75d118d00b0990586549949aae80cdc16624"},
- {file = "ruff-0.9.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a187171e7c09efa4b4cc30ee5d0d55a8d6c5311b3e1b74ac5cb96cc89bafc43c"},
- {file = "ruff-0.9.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c59ab92f8e92d6725b7ded9d4a31be3ef42688a115c6d3da9457a5bda140e2b4"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc153c25e715be41bb228bc651c1e9b1a88d5c6e5ed0194fa0dfea02b026439"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:646909a1e25e0dc28fbc529eab8eb7bb583079628e8cbe738192853dbbe43af5"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a5a46e09355695fbdbb30ed9889d6cf1c61b77b700a9fafc21b41f097bfbba4"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:c4bb09d2bbb394e3730d0918c00276e79b2de70ec2a5231cd4ebb51a57df9ba1"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:96a87ec31dc1044d8c2da2ebbed1c456d9b561e7d087734336518181b26b3aa5"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9bb7554aca6f842645022fe2d301c264e6925baa708b392867b7a62645304df4"},
- {file = "ruff-0.9.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cabc332b7075a914ecea912cd1f3d4370489c8018f2c945a30bcc934e3bc06a6"},
- {file = "ruff-0.9.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:33866c3cc2a575cbd546f2cd02bdd466fed65118e4365ee538a3deffd6fcb730"},
- {file = "ruff-0.9.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:006e5de2621304c8810bcd2ee101587712fa93b4f955ed0985907a36c427e0c2"},
- {file = "ruff-0.9.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ba6eea4459dbd6b1be4e6bfc766079fb9b8dd2e5a35aff6baee4d9b1514ea519"},
- {file = "ruff-0.9.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:90230a6b8055ad47d3325e9ee8f8a9ae7e273078a66401ac66df68943ced029b"},
- {file = "ruff-0.9.3-py3-none-win32.whl", hash = "sha256:eabe5eb2c19a42f4808c03b82bd313fc84d4e395133fb3fc1b1516170a31213c"},
- {file = "ruff-0.9.3-py3-none-win_amd64.whl", hash = "sha256:040ceb7f20791dfa0e78b4230ee9dce23da3b64dd5848e40e3bf3ab76468dcf4"},
- {file = "ruff-0.9.3-py3-none-win_arm64.whl", hash = "sha256:800d773f6d4d33b0a3c60e2c6ae8f4c202ea2de056365acfa519aa48acf28e0b"},
- {file = "ruff-0.9.3.tar.gz", hash = "sha256:8293f89985a090ebc3ed1064df31f3b4b56320cdfcec8b60d3295bddb955c22a"},
+ {file = "ruff-0.9.6-py3-none-linux_armv6l.whl", hash = "sha256:2f218f356dd2d995839f1941322ff021c72a492c470f0b26a34f844c29cdf5ba"},
+ {file = "ruff-0.9.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b908ff4df65dad7b251c9968a2e4560836d8f5487c2f0cc238321ed951ea0504"},
+ {file = "ruff-0.9.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b109c0ad2ececf42e75fa99dc4043ff72a357436bb171900714a9ea581ddef83"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de4367cca3dac99bcbd15c161404e849bb0bfd543664db39232648dc00112dc"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3ee4d7c2c92ddfdaedf0bf31b2b176fa7aa8950efc454628d477394d35638b"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5dc1edd1775270e6aa2386119aea692039781429f0be1e0949ea5884e011aa8e"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:4a091729086dffa4bd070aa5dab7e39cc6b9d62eb2bef8f3d91172d30d599666"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1bbc6808bf7b15796cef0815e1dfb796fbd383e7dbd4334709642649625e7c5"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:589d1d9f25b5754ff230dce914a174a7c951a85a4e9270613a2b74231fdac2f5"},
+ {file = "ruff-0.9.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc61dd5131742e21103fbbdcad683a8813be0e3c204472d520d9a5021ca8b217"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5e2d9126161d0357e5c8f30b0bd6168d2c3872372f14481136d13de9937f79b6"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:68660eab1a8e65babb5229a1f97b46e3120923757a68b5413d8561f8a85d4897"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:c4cae6c4cc7b9b4017c71114115db0445b00a16de3bcde0946273e8392856f08"},
+ {file = "ruff-0.9.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:19f505b643228b417c1111a2a536424ddde0db4ef9023b9e04a46ed8a1cb4656"},
+ {file = "ruff-0.9.6-py3-none-win32.whl", hash = "sha256:194d8402bceef1b31164909540a597e0d913c0e4952015a5b40e28c146121b5d"},
+ {file = "ruff-0.9.6-py3-none-win_amd64.whl", hash = "sha256:03482d5c09d90d4ee3f40d97578423698ad895c87314c4de39ed2af945633caa"},
+ {file = "ruff-0.9.6-py3-none-win_arm64.whl", hash = "sha256:0e2bb706a2be7ddfea4a4af918562fdc1bcb16df255e5fa595bbd800ce322a5a"},
+ {file = "ruff-0.9.6.tar.gz", hash = "sha256:81761592f72b620ec8fa1068a6fd00e98a5ebee342a3642efd84454f3031dca9"},
]
[[package]]
@@ -2449,7 +2449,7 @@ description = "Python bindings to FreeDesktop.org Secret Service API"
optional = false
python-versions = ">=3.6"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" and (python_version <= \"3.11\" or python_version >= \"3.12\")"
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\" or python_version >= \"3.12\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and sys_platform == \"linux\""
files = [
{file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"},
{file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"},
@@ -2798,7 +2798,6 @@ description = "A lil' TOML parser"
optional = false
python-versions = ">=3.8"
groups = ["main", "dev"]
-markers = "python_version < \"3.11\""
files = [
{file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"},
{file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"},
@@ -2833,6 +2832,7 @@ files = [
{file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"},
{file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"},
]
+markers = {main = "python_version < \"3.11\"", dev = "python_full_version <= \"3.11.0a6\""}
[[package]]
name = "tomlkit"
@@ -2849,15 +2849,15 @@ files = [
[[package]]
name = "trio"
-version = "0.28.0"
+version = "0.29.0"
description = "A friendly Python library for async concurrency and I/O"
optional = false
python-versions = ">=3.9"
groups = ["dev"]
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "trio-0.28.0-py3-none-any.whl", hash = "sha256:56d58977acc1635735a96581ec70513cc781b8b6decd299c487d3be2a721cd94"},
- {file = "trio-0.28.0.tar.gz", hash = "sha256:4e547896fe9e8a5658e54e4c7c5fa1db748cbbbaa7c965e7d40505b928c73c05"},
+ {file = "trio-0.29.0-py3-none-any.whl", hash = "sha256:d8c463f1a9cc776ff63e331aba44c125f423a5a13c684307e828d930e625ba66"},
+ {file = "trio-0.29.0.tar.gz", hash = "sha256:ea0d3967159fc130acb6939a0be0e558e364fee26b5deeecc893a6b08c361bdf"},
]
[package.dependencies]
@@ -2871,19 +2871,20 @@ sortedcontainers = "*"
[[package]]
name = "trio-websocket"
-version = "0.11.1"
+version = "0.12.1"
description = "WebSocket library for Trio"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
groups = ["dev"]
markers = "python_version <= \"3.11\" or python_version >= \"3.12\""
files = [
- {file = "trio-websocket-0.11.1.tar.gz", hash = "sha256:18c11793647703c158b1f6e62de638acada927344d534e3c7628eedcb746839f"},
- {file = "trio_websocket-0.11.1-py3-none-any.whl", hash = "sha256:520d046b0d030cf970b8b2b2e00c4c2245b3807853ecd44214acd33d74581638"},
+ {file = "trio_websocket-0.12.1-py3-none-any.whl", hash = "sha256:608ec746bb287e5d5a66baf483e41194193c5cf05ffaad6240e7d1fcd80d1e6f"},
+ {file = "trio_websocket-0.12.1.tar.gz", hash = "sha256:d55ccd4d3eae27c494f3fdae14823317839bdcb8214d1173eacc4d42c69fc91b"},
]
[package.dependencies]
exceptiongroup = {version = "*", markers = "python_version < \"3.11\""}
+outcome = ">=1.2.0"
trio = ">=0.11"
wsproto = ">=0.14"
@@ -3171,7 +3172,7 @@ description = "Backport of pathlib-compatible object wrapper for zip files"
optional = false
python-versions = ">=3.9"
groups = ["main"]
-markers = "(platform_machine != \"ppc64le\" and platform_machine != \"s390x\") and python_version <= \"3.11\" or python_full_version < \"3.10.2\""
+markers = "python_version <= \"3.11\" and (platform_machine != \"ppc64le\" and platform_machine != \"s390x\") or python_full_version < \"3.10.2\""
files = [
{file = "zipp-3.21.0-py3-none-any.whl", hash = "sha256:ac1bbe05fd2991f160ebce24ffbac5f6d11d83dc90891255885223d42b3cd931"},
{file = "zipp-3.21.0.tar.gz", hash = "sha256:2c9958f6430a2040341a52eb608ed6dd93ef4392e02ffe219417c1b28b5dd1f4"},
@@ -3188,4 +3189,4 @@ type = ["pytest-mypy"]
[metadata]
lock-version = "2.1"
python-versions = ">=3.10, <4.0"
-content-hash = "3b7e6e6e872c68f951f191d85a7d76fe1dd86caf32e2143a53a3152a3686fc7f"
+content-hash = "36de501672441a558232190e75c187dd92682b56a99fcd84dc2dc6ab78f7dfc8"
diff --git a/pyproject.toml b/pyproject.toml
index c192a1139..b3c63b7aa 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,23 +23,20 @@ fastapi = ">=0.96.0,!=0.111.0,!=0.111.1"
gunicorn = ">=20.1.0,<24.0"
jinja2 = ">=3.1.2,<4.0"
psutil = ">=5.9.4,<7.0"
-pydantic = ">=1.10.2,<3.0"
+pydantic = ">=1.10.21,<3.0"
python-multipart = ">=0.0.5,<0.1"
python-socketio = ">=5.7.0,<6.0"
redis = ">=4.3.5,<6.0"
rich = ">=13.0.0,<14.0"
sqlmodel = ">=0.0.14,<0.1"
-typer = ">=0.4.2,<1.0"
+typer = ">=0.15.1,<1.0"
uvicorn = ">=0.20.0"
starlette-admin = ">=0.11.0,<1.0"
alembic = ">=1.11.1,<2.0"
platformdirs = ">=3.10.0,<5.0"
distro = { version = ">=1.8.0,<2.0", platform = "linux" }
python-engineio = "!=4.6.0"
-wrapt = [
- { version = ">=1.14.0,<2.0", python = ">=3.11" },
- { version = ">=1.11.0,<2.0", python = "<3.11" },
-]
+wrapt = ">=1.17.0,<2.0"
packaging = ">=23.1,<25.0"
reflex-hosting-cli = ">=0.1.29"
charset-normalizer = ">=3.3.2,<4.0"
@@ -55,13 +52,13 @@ typing_extensions = ">=4.6.0"
[tool.poetry.group.dev.dependencies]
pytest = ">=7.1.2,<9.0"
pytest-mock = ">=3.10.0,<4.0"
-pyright = ">=1.1.392, <1.2"
+pyright = ">=1.1.394, <1.2"
darglint = ">=1.8.1,<2.0"
dill = ">=0.3.8"
toml = ">=0.10.2,<1.0"
pytest-asyncio = ">=0.24.0"
pytest-cov = ">=4.0.0,<7.0"
-ruff = "0.9.3"
+ruff = "0.9.6"
pandas = ">=2.1.1,<3.0"
pillow = ">=10.0.0,<12.0"
plotly = ">=5.13.0,<6.0"
@@ -87,8 +84,37 @@ reportIncompatibleMethodOverride = false
target-version = "py310"
output-format = "concise"
lint.isort.split-on-trailing-comma = false
-lint.select = ["ANN001","B", "C4", "D", "E", "ERA", "F", "FURB", "I", "N", "PERF", "PGH", "PTH", "RUF", "SIM", "T", "TRY", "W"]
-lint.ignore = ["B008", "D205", "E501", "F403", "SIM115", "RUF006", "RUF008", "RUF012", "TRY0"]
+lint.select = [
+ "ANN001",
+ "B",
+ "C4",
+ "D",
+ "E",
+ "ERA",
+ "F",
+ "FURB",
+ "I",
+ "N",
+ "PERF",
+ "PGH",
+ "PTH",
+ "RUF",
+ "SIM",
+ "T",
+ "TRY",
+ "W",
+]
+lint.ignore = [
+ "B008",
+ "D205",
+ "E501",
+ "F403",
+ "SIM115",
+ "RUF006",
+ "RUF008",
+ "RUF012",
+ "TRY0",
+]
lint.pydocstyle.convention = "google"
[tool.ruff.lint.per-file-ignores]
diff --git a/reflex/__init__.py b/reflex/__init__.py
index 3209b505e..7ee6f7e73 100644
--- a/reflex/__init__.py
+++ b/reflex/__init__.py
@@ -248,6 +248,7 @@ COMPONENTS_CORE_MAPPING: dict = {
"selected_files",
"upload",
],
+ "components.core.auto_scroll": ["auto_scroll"],
}
COMPONENTS_BASE_MAPPING: dict = {
diff --git a/reflex/__init__.pyi b/reflex/__init__.pyi
index 5c80269ad..1de63db6e 100644
--- a/reflex/__init__.pyi
+++ b/reflex/__init__.pyi
@@ -34,6 +34,7 @@ from .components.component import Component as Component
from .components.component import ComponentNamespace as ComponentNamespace
from .components.component import NoSSRComponent as NoSSRComponent
from .components.component import memo as memo
+from .components.core.auto_scroll import auto_scroll as auto_scroll
from .components.core.banner import connection_banner as connection_banner
from .components.core.banner import connection_modal as connection_modal
from .components.core.breakpoints import breakpoints as breakpoints
diff --git a/reflex/app.py b/reflex/app.py
index d290b8f49..03382751a 100644
--- a/reflex/app.py
+++ b/reflex/app.py
@@ -11,17 +11,17 @@ import functools
import inspect
import io
import json
-import multiprocessing
-import platform
import sys
import traceback
from datetime import datetime
from pathlib import Path
+from timeit import default_timer as timer
from types import SimpleNamespace
from typing import (
TYPE_CHECKING,
Any,
AsyncIterator,
+ BinaryIO,
Callable,
Coroutine,
Dict,
@@ -35,12 +35,15 @@ from typing import (
get_type_hints,
)
-from fastapi import FastAPI, HTTPException, Request, UploadFile
+from fastapi import FastAPI, HTTPException, Request
+from fastapi import UploadFile as FastAPIUploadFile
from fastapi.middleware import cors
from fastapi.responses import JSONResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from rich.progress import MofNCompleteColumn, Progress, TimeElapsedColumn
from socketio import ASGIApp, AsyncNamespace, AsyncServer
+from starlette.datastructures import Headers
+from starlette.datastructures import UploadFile as StarletteUploadFile
from starlette_admin.contrib.sqla.admin import Admin
from starlette_admin.contrib.sqla.view import ModelView
@@ -72,7 +75,7 @@ from reflex.components.core.client_side_routing import (
from reflex.components.core.sticky import sticky
from reflex.components.core.upload import Upload, get_upload_dir
from reflex.components.radix import themes
-from reflex.config import environment, get_config
+from reflex.config import ExecutorType, environment, get_config
from reflex.event import (
_EVENT_FIELDS,
Event,
@@ -108,7 +111,7 @@ from reflex.utils import (
prerequisites,
types,
)
-from reflex.utils.exec import is_prod_mode, is_testing_env
+from reflex.utils.exec import get_compile_context, is_prod_mode, is_testing_env
from reflex.utils.imports import ImportVar
if TYPE_CHECKING:
@@ -198,14 +201,17 @@ def default_overlay_component() -> Component:
Returns:
The default overlay_component, which is a connection_modal.
"""
- config = get_config()
from reflex.components.component import memo
def default_overlay_components():
return Fragment.create(
connection_pulser(),
connection_toaster(),
- *([backend_disabled()] if config.is_reflex_cloud else []),
+ *(
+ [backend_disabled()]
+ if get_compile_context() == constants.CompileContext.DEPLOY
+ else []
+ ),
*codespaces.codespaces_auto_redirect(),
)
@@ -231,6 +237,53 @@ class OverlayFragment(Fragment):
pass
+@dataclasses.dataclass(frozen=True)
+class UploadFile(StarletteUploadFile):
+ """A file uploaded to the server.
+
+ Args:
+ file: The standard Python file object (non-async).
+ filename: The original file name.
+ size: The size of the file in bytes.
+ headers: The headers of the request.
+ """
+
+ file: BinaryIO
+
+ path: Optional[Path] = dataclasses.field(default=None)
+
+ _deprecated_filename: Optional[str] = dataclasses.field(default=None)
+
+ size: Optional[int] = dataclasses.field(default=None)
+
+ headers: Headers = dataclasses.field(default_factory=Headers)
+
+ @property
+ def name(self) -> Optional[str]:
+ """Get the name of the uploaded file.
+
+ Returns:
+ The name of the uploaded file.
+ """
+ if self.path:
+ return self.path.name
+
+ @property
+ def filename(self) -> Optional[str]:
+ """Get the filename of the uploaded file.
+
+ Returns:
+ The filename of the uploaded file.
+ """
+ console.deprecate(
+ feature_name="UploadFile.filename",
+ reason="Use UploadFile.name instead.",
+ deprecation_version="0.7.1",
+ removal_version="0.8.0",
+ )
+ return self._deprecated_filename
+
+
@dataclasses.dataclass(
frozen=True,
)
@@ -591,7 +644,9 @@ class App(MiddlewareMixin, LifespanMixin):
Returns:
The generated component.
"""
- return component if isinstance(component, Component) else component()
+ from reflex.compiler.compiler import into_component
+
+ return into_component(component)
def add_page(
self,
@@ -1061,23 +1116,46 @@ class App(MiddlewareMixin, LifespanMixin):
app_wrappers[(1, "ToasterProvider")] = toast_provider
with console.timing("Evaluate Pages (Frontend)"):
+ performance_metrics: list[tuple[str, float]] = []
for route in self._unevaluated_pages:
console.debug(f"Evaluating page: {route}")
+ start = timer()
self._compile_page(route, save_page=should_compile)
+ end = timer()
+ performance_metrics.append((route, end - start))
progress.advance(task)
+ console.debug(
+ "Slowest pages:\n"
+ + "\n".join(
+ f"{route}: {time * 1000:.1f}ms"
+ for route, time in sorted(
+ performance_metrics, key=lambda x: x[1], reverse=True
+ )[:10]
+ )
+ )
# Add the optional endpoints (_upload)
self._add_optional_endpoints()
self._validate_var_dependencies()
self._setup_overlay_component()
+
+ if config.show_built_with_reflex is None:
+ if (
+ get_compile_context() == constants.CompileContext.DEPLOY
+ and prerequisites.get_user_tier() in ["pro", "team", "enterprise"]
+ ):
+ config.show_built_with_reflex = False
+ else:
+ config.show_built_with_reflex = True
+
if is_prod_mode() and config.show_built_with_reflex:
self._setup_sticky_badge()
progress.advance(task)
# Store the compile results.
- compile_results = []
+ compile_results: list[tuple[str, str]] = []
progress.advance(task)
@@ -1156,33 +1234,19 @@ class App(MiddlewareMixin, LifespanMixin):
),
)
- # Use a forking process pool, if possible. Much faster, especially for large sites.
- # Fallback to ThreadPoolExecutor as something that will always work.
- executor = None
- if (
- platform.system() in ("Linux", "Darwin")
- and (number_of_processes := environment.REFLEX_COMPILE_PROCESSES.get())
- is not None
- ):
- executor = concurrent.futures.ProcessPoolExecutor(
- max_workers=number_of_processes or None,
- mp_context=multiprocessing.get_context("fork"),
- )
- else:
- executor = concurrent.futures.ThreadPoolExecutor(
- max_workers=environment.REFLEX_COMPILE_THREADS.get() or None
- )
+ executor = ExecutorType.get_executor_from_environment()
for route, component in zip(self._pages, page_components, strict=True):
ExecutorSafeFunctions.COMPONENTS[route] = component
ExecutorSafeFunctions.STATE = self._state
- with executor:
- result_futures = []
+ with console.timing("Compile to Javascript"), executor as executor:
+ result_futures: list[concurrent.futures.Future[tuple[str, str]]] = []
- def _submit_work(fn: Callable, *args, **kwargs):
+ def _submit_work(fn: Callable[..., tuple[str, str]], *args, **kwargs):
f = executor.submit(fn, *args, **kwargs)
+ f.add_done_callback(lambda _: progress.advance(task))
result_futures.append(f)
# Compile the pre-compiled pages.
@@ -1208,10 +1272,10 @@ class App(MiddlewareMixin, LifespanMixin):
_submit_work(compiler.remove_tailwind_from_postcss)
# Wait for all compilation tasks to complete.
- with console.timing("Compile to Javascript"):
- for future in concurrent.futures.as_completed(result_futures):
- compile_results.append(future.result())
- progress.advance(task)
+ compile_results.extend(
+ future.result()
+ for future in concurrent.futures.as_completed(result_futures)
+ )
app_root = self._app_root(app_wrappers=app_wrappers)
@@ -1236,10 +1300,12 @@ class App(MiddlewareMixin, LifespanMixin):
progress.advance(task)
# Compile custom components.
- *custom_components_result, custom_components_imports = (
- compiler.compile_components(custom_components)
- )
- compile_results.append(custom_components_result)
+ (
+ custom_components_output,
+ custom_components_result,
+ custom_components_imports,
+ ) = compiler.compile_components(custom_components)
+ compile_results.append((custom_components_output, custom_components_result))
all_imports.update(custom_components_imports)
progress.advance(task)
@@ -1583,7 +1649,7 @@ def upload(app: App):
The upload function.
"""
- async def upload_file(request: Request, files: List[UploadFile]):
+ async def upload_file(request: Request, files: List[FastAPIUploadFile]):
"""Upload a file.
Args:
@@ -1659,7 +1725,8 @@ def upload(app: App):
file_copies.append(
UploadFile(
file=content_copy,
- filename=file.filename,
+ path=Path(file.filename.lstrip("/")) if file.filename else None,
+ _deprecated_filename=file.filename,
size=file.size,
headers=file.headers,
)
diff --git a/reflex/base.py b/reflex/base.py
index f6bbb8ce4..c900f0039 100644
--- a/reflex/base.py
+++ b/reflex/base.py
@@ -5,15 +5,9 @@ from __future__ import annotations
import os
from typing import TYPE_CHECKING, Any, List, Type
-try:
- import pydantic.v1.main as pydantic_main
- from pydantic.v1 import BaseModel
- from pydantic.v1.fields import ModelField
-except ModuleNotFoundError:
- if not TYPE_CHECKING:
- import pydantic.main as pydantic_main
- from pydantic import BaseModel
- from pydantic.fields import ModelField
+import pydantic.v1.main as pydantic_main
+from pydantic.v1 import BaseModel
+from pydantic.v1.fields import ModelField
def validate_field_name(bases: List[Type["BaseModel"]], field_name: str) -> None:
@@ -50,7 +44,7 @@ if TYPE_CHECKING:
from reflex.vars import Var
-class Base(BaseModel): # pyright: ignore [reportPossiblyUnboundVariable]
+class Base(BaseModel):
"""The base class subclassed by all Reflex classes.
This class wraps Pydantic and provides common methods such as
diff --git a/reflex/compiler/compiler.py b/reflex/compiler/compiler.py
index 7cd87fb71..81de50182 100644
--- a/reflex/compiler/compiler.py
+++ b/reflex/compiler/compiler.py
@@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import datetime
from pathlib import Path
-from typing import TYPE_CHECKING, Dict, Iterable, Optional, Tuple, Type, Union
+from typing import TYPE_CHECKING, Dict, Iterable, Optional, Sequence, Tuple, Type, Union
from reflex import constants
from reflex.compiler import templates, utils
@@ -508,7 +508,7 @@ def compile_tailwind(
The compiled Tailwind config.
"""
# Get the path for the output file.
- output_path = get_web_dir() / constants.Tailwind.CONFIG
+ output_path = str((get_web_dir() / constants.Tailwind.CONFIG).absolute())
# Compile the config.
code = _compile_tailwind(config)
@@ -545,7 +545,47 @@ def purge_web_pages_dir():
if TYPE_CHECKING:
- from reflex.app import UnevaluatedPage
+ from reflex.app import ComponentCallable, UnevaluatedPage
+
+
+def _into_component_once(component: Component | ComponentCallable) -> Component | None:
+ """Convert a component to a Component.
+
+ Args:
+ component: The component to convert.
+
+ Returns:
+ The converted component.
+ """
+ if isinstance(component, Component):
+ return component
+ if isinstance(component, (Var, int, float, str)):
+ return Fragment.create(component)
+ if isinstance(component, Sequence):
+ return Fragment.create(*component)
+ return None
+
+
+def into_component(component: Component | ComponentCallable) -> Component:
+ """Convert a component to a Component.
+
+ Args:
+ component: The component to convert.
+
+ Returns:
+ The converted component.
+
+ Raises:
+ TypeError: If the component is not a Component.
+ """
+ if (converted := _into_component_once(component)) is not None:
+ return converted
+ if (
+ callable(component)
+ and (converted := _into_component_once(component())) is not None
+ ):
+ return converted
+ raise TypeError(f"Expected a Component, got {type(component)}")
def compile_unevaluated_page(
@@ -568,12 +608,7 @@ def compile_unevaluated_page(
The compiled component and whether state should be enabled.
"""
# Generate the component if it is a callable.
- component = page.component
- component = component if isinstance(component, Component) else component()
-
- # unpack components that return tuples in an rx.fragment.
- if isinstance(component, tuple):
- component = Fragment.create(*component)
+ component = into_component(page.component)
component._add_style_recursive(style or {}, theme)
@@ -678,10 +713,8 @@ class ExecutorSafeFunctions:
The route, compiled component, and compiled page.
"""
component, enable_state = compile_unevaluated_page(
- route, cls.UNCOMPILED_PAGES[route]
+ route, cls.UNCOMPILED_PAGES[route], cls.STATE, style, theme
)
- component = component if isinstance(component, Component) else component()
- component._add_style_recursive(style, theme)
return route, component, compile_page(route, component, cls.STATE)
@classmethod
diff --git a/reflex/compiler/utils.py b/reflex/compiler/utils.py
index 91ee18b86..cd6997e22 100644
--- a/reflex/compiler/utils.py
+++ b/reflex/compiler/utils.py
@@ -10,16 +10,7 @@ from pathlib import Path
from typing import Any, Callable, Dict, Optional, Type, Union
from urllib.parse import urlparse
-from reflex.utils.exec import is_in_app_harness
-from reflex.utils.prerequisites import get_web_dir
-from reflex.vars.base import Var
-
-try:
- from pydantic.v1.fields import ModelField
-except ModuleNotFoundError:
- from pydantic.fields import (
- ModelField, # pyright: ignore [reportAttributeAccessIssue]
- )
+from pydantic.v1.fields import ModelField
from reflex import constants
from reflex.components.base import (
@@ -39,7 +30,10 @@ from reflex.istate.storage import Cookie, LocalStorage, SessionStorage
from reflex.state import BaseState, _resolve_delta
from reflex.style import Style
from reflex.utils import console, format, imports, path_ops
+from reflex.utils.exec import is_in_app_harness
from reflex.utils.imports import ImportVar, ParsedImportDict
+from reflex.utils.prerequisites import get_web_dir
+from reflex.vars.base import Var
# To re-export this function.
merge_imports = imports.merge_imports
@@ -523,6 +517,8 @@ def write_page(path: str | Path, code: str):
"""
path = Path(path)
path_ops.mkdir(path.parent)
+ if path.exists() and path.read_text(encoding="utf-8") == code:
+ return
path.write_text(code, encoding="utf-8")
diff --git a/reflex/components/component.py b/reflex/components/component.py
index d27bddf78..005f7791d 100644
--- a/reflex/components/component.py
+++ b/reflex/components/component.py
@@ -51,13 +51,7 @@ from reflex.event import (
)
from reflex.style import Style, format_as_emotion
from reflex.utils import format, imports, types
-from reflex.utils.imports import (
- ImmutableParsedImportDict,
- ImportDict,
- ImportVar,
- ParsedImportDict,
- parse_imports,
-)
+from reflex.utils.imports import ImportDict, ImportVar, ParsedImportDict, parse_imports
from reflex.vars import VarData
from reflex.vars.base import (
CachedVarOperation,
@@ -1208,7 +1202,7 @@ class Component(BaseComponent, ABC):
Returns:
True if the dependency should be transpiled.
"""
- return (
+ return bool(self.transpile_packages) and (
dep in self.transpile_packages
or format.format_library_name(dep or "") in self.transpile_packages
)
@@ -1291,9 +1285,10 @@ class Component(BaseComponent, ABC):
event_imports = Imports.EVENTS if self.event_triggers else {}
# Collect imports from Vars used directly by this component.
- var_datas = [var._get_all_var_data() for var in self._get_vars()]
- var_imports: List[ImmutableParsedImportDict] = [
- var_data.imports for var_data in var_datas if var_data is not None
+ var_imports = [
+ var_data.imports
+ for var in self._get_vars()
+ if (var_data := var._get_all_var_data()) is not None
]
added_import_dicts: list[ParsedImportDict] = []
diff --git a/reflex/components/core/__init__.py b/reflex/components/core/__init__.py
index fbe0bdc84..d1f822e67 100644
--- a/reflex/components/core/__init__.py
+++ b/reflex/components/core/__init__.py
@@ -48,6 +48,7 @@ _SUBMOD_ATTRS: dict[str, list[str]] = {
"get_upload_url",
"selected_files",
],
+ "auto_scroll": ["auto_scroll"],
}
__getattr__, __dir__, __all__ = lazy_loader.attach(
diff --git a/reflex/components/core/__init__.pyi b/reflex/components/core/__init__.pyi
index ea9275334..e94b4982e 100644
--- a/reflex/components/core/__init__.pyi
+++ b/reflex/components/core/__init__.pyi
@@ -4,6 +4,7 @@
# ------------------------------------------------------
from . import layout as layout
+from .auto_scroll import auto_scroll as auto_scroll
from .banner import ConnectionBanner as ConnectionBanner
from .banner import ConnectionModal as ConnectionModal
from .banner import ConnectionPulser as ConnectionPulser
diff --git a/reflex/components/core/auto_scroll.py b/reflex/components/core/auto_scroll.py
new file mode 100644
index 000000000..d24bd9a13
--- /dev/null
+++ b/reflex/components/core/auto_scroll.py
@@ -0,0 +1,111 @@
+"""A component that automatically scrolls to the bottom when new content is added."""
+
+from __future__ import annotations
+
+from reflex.components.el.elements.typography import Div
+from reflex.constants.compiler import MemoizationDisposition, MemoizationMode
+from reflex.utils.imports import ImportDict
+from reflex.vars.base import Var, get_unique_variable_name
+
+
+class AutoScroll(Div):
+ """A div that automatically scrolls to the bottom when new content is added."""
+
+ _memoization_mode = MemoizationMode(disposition=MemoizationDisposition.ALWAYS)
+
+ @classmethod
+ def create(cls, *children, **props):
+ """Create an AutoScroll component.
+
+ Args:
+ *children: The children of the component.
+ **props: The props of the component.
+
+ Returns:
+ An AutoScroll component.
+ """
+ props.setdefault("overflow", "auto")
+ props.setdefault("id", get_unique_variable_name())
+ return super().create(*children, **props)
+
+ def add_imports(self) -> ImportDict | list[ImportDict]:
+ """Add imports required for the component.
+
+ Returns:
+ The imports required for the component.
+ """
+ return {"react": ["useEffect", "useRef"]}
+
+ def add_hooks(self) -> list[str | Var]:
+ """Add hooks required for the component.
+
+ Returns:
+ The hooks required for the component.
+ """
+ ref_name = self.get_ref()
+ return [
+ "const containerRef = useRef(null);",
+ "const wasNearBottom = useRef(false);",
+ "const hadScrollbar = useRef(false);",
+ f"""
+const checkIfNearBottom = () => {{
+ if (!{ref_name}.current) return;
+
+ const container = {ref_name}.current;
+ const nearBottomThreshold = 50; // pixels from bottom to trigger auto-scroll
+
+ const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
+
+ wasNearBottom.current = distanceFromBottom <= nearBottomThreshold;
+
+ // Track if container had a scrollbar
+ hadScrollbar.current = container.scrollHeight > container.clientHeight;
+}};
+""",
+ f"""
+const scrollToBottomIfNeeded = () => {{
+ if (!{ref_name}.current) return;
+
+ const container = {ref_name}.current;
+ const hasScrollbarNow = container.scrollHeight > container.clientHeight;
+
+ // Scroll if:
+ // 1. User was near bottom, OR
+ // 2. Container didn't have scrollbar before but does now
+ if (wasNearBottom.current || (!hadScrollbar.current && hasScrollbarNow)) {{
+ container.scrollTop = container.scrollHeight;
+ }}
+
+ // Update scrollbar state for next check
+ hadScrollbar.current = hasScrollbarNow;
+}};
+""",
+ f"""
+useEffect(() => {{
+ const container = {ref_name}.current;
+ if (!container) return;
+
+ // Create ResizeObserver to detect height changes
+ const resizeObserver = new ResizeObserver(() => {{
+ scrollToBottomIfNeeded();
+ }});
+
+ // Track scroll position before height changes
+ container.addEventListener('scroll', checkIfNearBottom);
+
+ // Initial check
+ checkIfNearBottom();
+
+ // Observe container for size changes
+ resizeObserver.observe(container);
+
+ return () => {{
+ container.removeEventListener('scroll', checkIfNearBottom);
+ resizeObserver.disconnect();
+ }};
+}});
+""",
+ ]
+
+
+auto_scroll = AutoScroll.create
diff --git a/reflex/components/core/auto_scroll.pyi b/reflex/components/core/auto_scroll.pyi
new file mode 100644
index 000000000..c34ed32d6
--- /dev/null
+++ b/reflex/components/core/auto_scroll.pyi
@@ -0,0 +1,284 @@
+"""Stub file for reflex/components/core/auto_scroll.py"""
+
+# ------------------- DO NOT EDIT ----------------------
+# This file was generated by `reflex/utils/pyi_generator.py`!
+# ------------------------------------------------------
+from typing import Any, Dict, Literal, Optional, Union, overload
+
+from reflex.components.el.elements.typography import Div
+from reflex.event import EventType
+from reflex.style import Style
+from reflex.utils.imports import ImportDict
+from reflex.vars.base import Var
+
+class AutoScroll(Div):
+ @overload
+ @classmethod
+ def create( # type: ignore
+ cls,
+ *children,
+ access_key: Optional[Union[Var[str], str]] = None,
+ auto_capitalize: Optional[
+ Union[
+ Literal["characters", "none", "off", "on", "sentences", "words"],
+ Var[Literal["characters", "none", "off", "on", "sentences", "words"]],
+ ]
+ ] = None,
+ content_editable: Optional[
+ Union[
+ Literal["inherit", "plaintext-only", False, True],
+ Var[Literal["inherit", "plaintext-only", False, True]],
+ ]
+ ] = None,
+ context_menu: Optional[Union[Var[str], str]] = None,
+ dir: Optional[Union[Var[str], str]] = None,
+ draggable: Optional[Union[Var[bool], bool]] = None,
+ enter_key_hint: Optional[
+ Union[
+ Literal["done", "enter", "go", "next", "previous", "search", "send"],
+ Var[
+ Literal["done", "enter", "go", "next", "previous", "search", "send"]
+ ],
+ ]
+ ] = None,
+ hidden: Optional[Union[Var[bool], bool]] = None,
+ input_mode: Optional[
+ Union[
+ Literal[
+ "decimal",
+ "email",
+ "none",
+ "numeric",
+ "search",
+ "tel",
+ "text",
+ "url",
+ ],
+ Var[
+ Literal[
+ "decimal",
+ "email",
+ "none",
+ "numeric",
+ "search",
+ "tel",
+ "text",
+ "url",
+ ]
+ ],
+ ]
+ ] = None,
+ item_prop: Optional[Union[Var[str], str]] = None,
+ lang: Optional[Union[Var[str], str]] = None,
+ role: Optional[
+ Union[
+ Literal[
+ "alert",
+ "alertdialog",
+ "application",
+ "article",
+ "banner",
+ "button",
+ "cell",
+ "checkbox",
+ "columnheader",
+ "combobox",
+ "complementary",
+ "contentinfo",
+ "definition",
+ "dialog",
+ "directory",
+ "document",
+ "feed",
+ "figure",
+ "form",
+ "grid",
+ "gridcell",
+ "group",
+ "heading",
+ "img",
+ "link",
+ "list",
+ "listbox",
+ "listitem",
+ "log",
+ "main",
+ "marquee",
+ "math",
+ "menu",
+ "menubar",
+ "menuitem",
+ "menuitemcheckbox",
+ "menuitemradio",
+ "navigation",
+ "none",
+ "note",
+ "option",
+ "presentation",
+ "progressbar",
+ "radio",
+ "radiogroup",
+ "region",
+ "row",
+ "rowgroup",
+ "rowheader",
+ "scrollbar",
+ "search",
+ "searchbox",
+ "separator",
+ "slider",
+ "spinbutton",
+ "status",
+ "switch",
+ "tab",
+ "table",
+ "tablist",
+ "tabpanel",
+ "term",
+ "textbox",
+ "timer",
+ "toolbar",
+ "tooltip",
+ "tree",
+ "treegrid",
+ "treeitem",
+ ],
+ Var[
+ Literal[
+ "alert",
+ "alertdialog",
+ "application",
+ "article",
+ "banner",
+ "button",
+ "cell",
+ "checkbox",
+ "columnheader",
+ "combobox",
+ "complementary",
+ "contentinfo",
+ "definition",
+ "dialog",
+ "directory",
+ "document",
+ "feed",
+ "figure",
+ "form",
+ "grid",
+ "gridcell",
+ "group",
+ "heading",
+ "img",
+ "link",
+ "list",
+ "listbox",
+ "listitem",
+ "log",
+ "main",
+ "marquee",
+ "math",
+ "menu",
+ "menubar",
+ "menuitem",
+ "menuitemcheckbox",
+ "menuitemradio",
+ "navigation",
+ "none",
+ "note",
+ "option",
+ "presentation",
+ "progressbar",
+ "radio",
+ "radiogroup",
+ "region",
+ "row",
+ "rowgroup",
+ "rowheader",
+ "scrollbar",
+ "search",
+ "searchbox",
+ "separator",
+ "slider",
+ "spinbutton",
+ "status",
+ "switch",
+ "tab",
+ "table",
+ "tablist",
+ "tabpanel",
+ "term",
+ "textbox",
+ "timer",
+ "toolbar",
+ "tooltip",
+ "tree",
+ "treegrid",
+ "treeitem",
+ ]
+ ],
+ ]
+ ] = None,
+ slot: Optional[Union[Var[str], str]] = None,
+ spell_check: Optional[Union[Var[bool], bool]] = None,
+ tab_index: Optional[Union[Var[int], int]] = None,
+ title: Optional[Union[Var[str], str]] = None,
+ style: Optional[Style] = None,
+ key: Optional[Any] = None,
+ id: Optional[Any] = None,
+ class_name: Optional[Any] = None,
+ autofocus: Optional[bool] = None,
+ custom_attrs: Optional[Dict[str, Union[Var, Any]]] = None,
+ on_blur: Optional[EventType[()]] = None,
+ on_click: Optional[EventType[()]] = None,
+ on_context_menu: Optional[EventType[()]] = None,
+ on_double_click: Optional[EventType[()]] = None,
+ on_focus: Optional[EventType[()]] = None,
+ on_mount: Optional[EventType[()]] = None,
+ on_mouse_down: Optional[EventType[()]] = None,
+ on_mouse_enter: Optional[EventType[()]] = None,
+ on_mouse_leave: Optional[EventType[()]] = None,
+ on_mouse_move: Optional[EventType[()]] = None,
+ on_mouse_out: Optional[EventType[()]] = None,
+ on_mouse_over: Optional[EventType[()]] = None,
+ on_mouse_up: Optional[EventType[()]] = None,
+ on_scroll: Optional[EventType[()]] = None,
+ on_unmount: Optional[EventType[()]] = None,
+ **props,
+ ) -> "AutoScroll":
+ """Create an AutoScroll component.
+
+ Args:
+ *children: The children of the component.
+ access_key: Provides a hint for generating a keyboard shortcut for the current element.
+ auto_capitalize: Controls whether and how text input is automatically capitalized as it is entered/edited by the user.
+ content_editable: Indicates whether the element's content is editable.
+ context_menu: Defines the ID of a