From b8f4e61050d96fbb32bed2fca4a9618584c3c0dc Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 15:00:25 +0000 Subject: [PATCH 01/60] Benchmark with app harness --- integration/benchmarks/test_blank_app.py | 88 ++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 integration/benchmarks/test_blank_app.py diff --git a/integration/benchmarks/test_blank_app.py b/integration/benchmarks/test_blank_app.py new file mode 100644 index 0000000000..5cd922cc39 --- /dev/null +++ b/integration/benchmarks/test_blank_app.py @@ -0,0 +1,88 @@ +import pytest +import time + +from typing import Generator + +import pytest +from selenium.webdriver.common.by import By + +from reflex.testing import DEFAULT_TIMEOUT, AppHarness, WebDriver, chdir + + +def BlankTemplate(): + """Test that background tasks work as expected.""" + from rxconfig import config + + 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.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", + ) + + app = rx.App(state=rx.State) + app.add_page(index) + + +@pytest.fixture(scope="session") +def blank_template( + 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(f"blank_template") + print(f" root dir: {root}") + with AppHarness.create( + root=root, + app_source=BlankTemplate, # type: ignore + ) as harness: + yield harness + + + +@pytest.mark.benchmark( + group="blank template", + min_time=0.1, + max_time=0.5, + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False +) +def test_blank_template_app_start(benchmark, blank_template, mocker): + import copy + assert blank_template.app_instance is not None + from reflex.constants.base import Dirs + dirs = copy.deepcopy(Dirs) + + dirs.WEB = str( blank_template.app_path /".web") + dirs.UTILS = "/".join([str( blank_template.app_path),"utils"]) + + mocker.patch("reflex.utils.prerequisites.constants.Dirs", dirs) + benchmark(blank_template.app_instance.compile_) \ No newline at end of file From fdc0595a9eb4b8a51e5a961453fd1b9fd1dfcc03 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 15:28:57 +0000 Subject: [PATCH 02/60] update matrix --- .github/workflows/benchmarks.yml | 21 ++++++++++++++++++--- integration/benchmarks/test_blank_app.py | 1 - 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 7c61f0f181..d0bbb1f2c4 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -26,13 +26,28 @@ jobs: reflex-web: env: DATABASE_URL: ${{ secrets.DATABASE_URL }} + timeout-minutes: 30 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] - python-version: ['3.11.4'] - node-version: ['16.x'] + os: [ ubuntu-latest, windows-latest, macos-latest ] + python-version: [ '3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] + exclude: + - os: windows-latest + python-version: '3.10.13' + - os: windows-latest + python-version: '3.9.18' + - os: windows-latest + python-version: '3.8.18' + include: + - os: windows-latest + python-version: '3.10.11' + - os: windows-latest + python-version: '3.9.13' + - os: windows-latest + python-version: '3.8.10' runs-on: ${{ matrix.os }} steps: diff --git a/integration/benchmarks/test_blank_app.py b/integration/benchmarks/test_blank_app.py index 5cd922cc39..9b556e7c68 100644 --- a/integration/benchmarks/test_blank_app.py +++ b/integration/benchmarks/test_blank_app.py @@ -57,7 +57,6 @@ def blank_template( running AppHarness instance """ root = tmp_path_factory.mktemp(f"blank_template") - print(f" root dir: {root}") with AppHarness.create( root=root, app_source=BlankTemplate, # type: ignore From 5d7ef3372b5109f97757d5364fcb9ba3fd668d8f Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 15:41:55 +0000 Subject: [PATCH 03/60] switch order --- .github/workflows/benchmarks.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index d0bbb1f2c4..20d0511848 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -62,6 +62,15 @@ jobs: run-poetry-install: true create-venv-at-path: .venv + - name: Run Benchmarks + # Only run if the database creds are available in this context. + if: ${{ env.DATABASE_URL }} + working-directory: ./integration/benchmarks + run: poetry run python benchmarks.py "$GITHUB_SHA" .lighthouseci + env: + GITHUB_SHA: ${{ github.sha }} + PR_TITLE: ${{ github.event.pull_request.title }} + - name: Clone Reflex Website Repo uses: actions/checkout@v4 with: @@ -82,11 +91,4 @@ jobs: poetry run bash scripts/benchmarks.sh ./reflex-web prod env: LHCI_GITHUB_APP_TOKEN: $ - - name: Run Benchmarks - # Only run if the database creds are available in this context. - if: ${{ env.DATABASE_URL }} - working-directory: ./integration/benchmarks - run: poetry run python benchmarks.py "$GITHUB_SHA" .lighthouseci - env: - GITHUB_SHA: ${{ github.sha }} - PR_TITLE: ${{ github.event.pull_request.title }} + From 7169a35d910f39be923475a8cec502c4c8fbde10 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 15:47:05 +0000 Subject: [PATCH 04/60] install psycopg2 --- .github/workflows/benchmarks.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 20d0511848..404fc4e0dd 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -62,6 +62,7 @@ jobs: run-poetry-install: true create-venv-at-path: .venv + - run: poetry run pip install psycopg2 - name: Run Benchmarks # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} From d8d2018f820d589f099bb5a5a8e0fdca1784059e Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 15:59:58 +0000 Subject: [PATCH 05/60] modify workflow --- .github/workflows/benchmarks.yml | 75 ++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 404fc4e0dd..3e3c1455ef 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -26,28 +26,13 @@ jobs: reflex-web: env: DATABASE_URL: ${{ secrets.DATABASE_URL }} - timeout-minutes: 30 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.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] - exclude: - - os: windows-latest - python-version: '3.10.13' - - os: windows-latest - python-version: '3.9.18' - - os: windows-latest - python-version: '3.8.18' - include: - - os: windows-latest - python-version: '3.10.11' - - os: windows-latest - python-version: '3.9.13' - - os: windows-latest - python-version: '3.8.10' + os: [ubuntu-latest] + python-version: ['3.11.4'] + node-version: ['16.x'] runs-on: ${{ matrix.os }} steps: @@ -62,16 +47,6 @@ jobs: run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip install psycopg2 - - name: Run Benchmarks - # Only run if the database creds are available in this context. - if: ${{ env.DATABASE_URL }} - working-directory: ./integration/benchmarks - run: poetry run python benchmarks.py "$GITHUB_SHA" .lighthouseci - env: - GITHUB_SHA: ${{ github.sha }} - PR_TITLE: ${{ github.event.pull_request.title }} - - name: Clone Reflex Website Repo uses: actions/checkout@v4 with: @@ -93,3 +68,47 @@ jobs: env: LHCI_GITHUB_APP_TOKEN: $ + + reflex-benchmarks: + env: + DATABASE_URL: ${{ secrets.DATABASE_URL }} + timeout-minutes: 30 + 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.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] + exclude: + - os: windows-latest + python-version: '3.10.13' + - os: windows-latest + python-version: '3.9.18' + - os: windows-latest + python-version: '3.8.18' + include: + - os: windows-latest + python-version: '3.10.11' + - os: windows-latest + python-version: '3.9.13' + - os: windows-latest + python-version: '3.8.10' + + 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 + - run: poetry run pip pyscopg2 + - name: Run Benchmarks + # Only run if the database creds are available in this context. + if: ${{ env.DATABASE_URL }} + working-directory: ./integration/benchmarks + run: poetry run python benchmarks.py "$GITHUB_SHA" .lighthouseci + env: + GITHUB_SHA: ${{ github.sha }} + PR_TITLE: ${{ github.event.pull_request.title }} From 041819bba1ca601b0a48c0a8175ff76cb2abe6ab Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 16:02:52 +0000 Subject: [PATCH 06/60] its psycopg2 --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 3e3c1455ef..28beb9f120 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -103,7 +103,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip pyscopg2 + - run: poetry run pip psyscopg2 - name: Run Benchmarks # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} From f70a7e30a1280489b59b07742b8273d5acb1de4d Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 16:04:05 +0000 Subject: [PATCH 07/60] its psycopg2 --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 28beb9f120..74e6dfa800 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -103,7 +103,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip psyscopg2 + - run: poetry run pip psycopg2 - name: Run Benchmarks # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} From 60244253105efab3b149b8cb3b8963f0645c0cd1 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 16:06:31 +0000 Subject: [PATCH 08/60] add install cmd --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 74e6dfa800..51149bbd80 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -103,7 +103,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip psycopg2 + - run: poetry run pip install psycopg2 - name: Run Benchmarks # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} From d02e50bd5b9cc7d48b289214b2e530bc66ddd643 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 16:14:06 +0000 Subject: [PATCH 09/60] python 3.8 type issues --- integration/benchmarks/helpers.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/integration/benchmarks/helpers.py b/integration/benchmarks/helpers.py index e1a3f493b5..8708629500 100644 --- a/integration/benchmarks/helpers.py +++ b/integration/benchmarks/helpers.py @@ -2,14 +2,15 @@ import json from datetime import datetime +from typing import List, Dict import psycopg2 def insert_benchmarking_data( db_connection_url: str, - lighthouse_data: dict, - performance_data: list[dict], + lighthouse_data: Dict, + performance_data: List[Dict], commit_sha: str, pr_title: str, ): From 96280bf106107f95b6731c189248af85cf8def68 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 4 Mar 2024 16:16:12 +0000 Subject: [PATCH 10/60] python 3.8 type issues --- integration/benchmarks/benchmarks.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/integration/benchmarks/benchmarks.py b/integration/benchmarks/benchmarks.py index 787e959703..7c735fe46c 100644 --- a/integration/benchmarks/benchmarks.py +++ b/integration/benchmarks/benchmarks.py @@ -3,12 +3,13 @@ import json import os import sys +from typing import List, Dict import pytest from helpers import insert_benchmarking_data -def get_lighthouse_scores(directory_path: str) -> dict: +def get_lighthouse_scores(directory_path: str) -> Dict: """Extracts the Lighthouse scores from the JSON files in the specified directory. Args: @@ -44,7 +45,7 @@ def get_lighthouse_scores(directory_path: str) -> dict: return scores -def run_pytest_and_get_results(test_path=None) -> dict: +def run_pytest_and_get_results(test_path=None) -> List: """Runs pytest and returns the results. Args: @@ -71,7 +72,7 @@ def run_pytest_and_get_results(test_path=None) -> dict: return pytest_results -def extract_stats_from_json(json_data) -> list[dict]: +def extract_stats_from_json(json_data) -> List[Dict]: """Extracts the stats from the JSON data and returns them as a list of dictionaries. Args: From 39bc0881e669cb28137f1d28ccc897a50eb04d75 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:22:28 +0000 Subject: [PATCH 11/60] use codspeed --- .github/workflows/benchmarks.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 51149bbd80..b463357207 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -106,9 +106,5 @@ jobs: - run: poetry run pip install psycopg2 - name: Run Benchmarks # Only run if the database creds are available in this context. - if: ${{ env.DATABASE_URL }} - working-directory: ./integration/benchmarks - run: poetry run python benchmarks.py "$GITHUB_SHA" .lighthouseci - env: - GITHUB_SHA: ${{ github.sha }} - PR_TITLE: ${{ github.event.pull_request.title }} + uses: CodSpeedHQ/action@v2 + run: pytest integration/benchmark --codspeed From 69aa25d31c7893e71581e888bd9d41bc0acba9d3 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:42:26 +0000 Subject: [PATCH 12/60] poetry run --- .github/workflows/benchmarks.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index b463357207..dac6dc74f4 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -70,8 +70,6 @@ jobs: reflex-benchmarks: - env: - DATABASE_URL: ${{ secrets.DATABASE_URL }} timeout-minutes: 30 strategy: # Prioritize getting more information out of the workflow (even if something fails) @@ -107,4 +105,4 @@ jobs: - name: Run Benchmarks # Only run if the database creds are available in this context. uses: CodSpeedHQ/action@v2 - run: pytest integration/benchmark --codspeed + run: poetry run pytest integration/benchmark/ --codspeed From 533c8ff90cc90913f9439f7c0579575e94886045 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:44:55 +0000 Subject: [PATCH 13/60] fix benchmark workflow syntax --- .github/workflows/benchmarks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index dac6dc74f4..e16b12ee32 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -105,4 +105,5 @@ jobs: - name: Run Benchmarks # Only run if the database creds are available in this context. uses: CodSpeedHQ/action@v2 - run: poetry run pytest integration/benchmark/ --codspeed + with: + run: poetry run pytest integration/benchmark/ --codspeed From 60738c968c59adc6b2969027b3e115c1a698cb2a Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:51:05 +0000 Subject: [PATCH 14/60] add codspeed deps --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index f38391d0fa..61f67bfb70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,6 +85,7 @@ pre-commit = {version = "^3.2.1", python = ">=3.8,<4.0"} selenium = "^4.11.0" types-tabulate = "^0.9.0.3" pytest-benchmark = "^4.0.0" +pytest-codspeed = "^2.2.0" [tool.poetry.scripts] reflex = "reflex.reflex:cli" From 16860c0f07efdd003110072f82c58ec74b7932fc Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:53:49 +0000 Subject: [PATCH 15/60] manually install codspeed --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index e16b12ee32..6d97b7189b 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -101,7 +101,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip install psycopg2 + - run: poetry run pip install pytest-codspeed - name: Run Benchmarks # Only run if the database creds are available in this context. uses: CodSpeedHQ/action@v2 From d50d7b413410551583242530e2ff34c31f00016b Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 01:58:29 +0000 Subject: [PATCH 16/60] fix test dir path --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 6d97b7189b..8e04b723d2 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -106,4 +106,4 @@ jobs: # Only run if the database creds are available in this context. uses: CodSpeedHQ/action@v2 with: - run: poetry run pytest integration/benchmark/ --codspeed + run: poetry run pytest integration/benchmarks/ --codspeed From 0854292742e58fc5d365bc5df9a98a2aced1e0ff Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 02:06:57 +0000 Subject: [PATCH 17/60] install missing deps --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 8e04b723d2..4b6a6932aa 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -101,7 +101,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip install pytest-codspeed + - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow - name: Run Benchmarks # Only run if the database creds are available in this context. uses: CodSpeedHQ/action@v2 From 6e61bb1509229b87fdc85dba3e935fea76706f02 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 02:28:53 +0000 Subject: [PATCH 18/60] use codspeed.yml instead --- .github/workflows/benchmarks.yml | 40 ----------------------------- .github/workflows/codspeed.yml | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 40 deletions(-) create mode 100644 .github/workflows/codspeed.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 4b6a6932aa..5b98280155 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -67,43 +67,3 @@ jobs: poetry run bash scripts/benchmarks.sh ./reflex-web prod env: LHCI_GITHUB_APP_TOKEN: $ - - - reflex-benchmarks: - timeout-minutes: 30 - 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.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] - exclude: - - os: windows-latest - python-version: '3.10.13' - - os: windows-latest - python-version: '3.9.18' - - os: windows-latest - python-version: '3.8.18' - include: - - os: windows-latest - python-version: '3.10.11' - - os: windows-latest - python-version: '3.9.13' - - os: windows-latest - python-version: '3.8.10' - - 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 - - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow - - name: Run Benchmarks - # Only run if the database creds are available in this context. - uses: CodSpeedHQ/action@v2 - with: - run: poetry run pytest integration/benchmarks/ --codspeed diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml new file mode 100644 index 0000000000..e08a52c24e --- /dev/null +++ b/.github/workflows/codspeed.yml @@ -0,0 +1,43 @@ +name: codspeed-benchmarks + +on: + push: + branches: [main] + paths-ignore: + - '**/*.md' + pull_request: + branches: [main] + paths-ignore: + - '**/*.md' + +permissions: + contents: read + +defaults: + run: + shell: bash + +jobs: + benchmarks: + 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 ] + python-version: [ '3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] + + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + run-poetry-install: true + create-venv-at-path: .venv + + - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow + - name: Run benchmarks + uses: CodSpeedHQ/action@v2 + with: + run: poetry run pytest tests/ --codspeed From 7dc6899d0fdc41553559d847bf75b0072c48ff78 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 02:33:22 +0000 Subject: [PATCH 19/60] poetry setup in CI --- .github/workflows/codspeed.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index e08a52c24e..435e8308d4 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -29,8 +29,8 @@ jobs: runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v3 + - uses: actions/checkout@v4 + - uses: ./.github/actions/setup_build_env with: python-version: ${{ matrix.python-version }} run-poetry-install: true From 37f0df0408a9284a4d77839be9d6f4972b6df2d4 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 02:36:57 +0000 Subject: [PATCH 20/60] fix test path --- .github/workflows/codspeed.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml index 435e8308d4..e7cc42019f 100644 --- a/.github/workflows/codspeed.yml +++ b/.github/workflows/codspeed.yml @@ -40,4 +40,4 @@ jobs: - name: Run benchmarks uses: CodSpeedHQ/action@v2 with: - run: poetry run pytest tests/ --codspeed + run: poetry run pytest integration/benchmarks/ --codspeed From 3eb36f2f13da8d94dcf5b16da5fba459960320cc Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 14:30:05 +0000 Subject: [PATCH 21/60] add test for blank template with two pages --- integration/benchmarks/benchmarks.py | 2 +- integration/benchmarks/helpers.py | 2 +- integration/benchmarks/test_blank_app.py | 182 +++++++++++++++++++---- 3 files changed, 159 insertions(+), 27 deletions(-) diff --git a/integration/benchmarks/benchmarks.py b/integration/benchmarks/benchmarks.py index 7c735fe46c..6a9b67f072 100644 --- a/integration/benchmarks/benchmarks.py +++ b/integration/benchmarks/benchmarks.py @@ -3,7 +3,7 @@ import json import os import sys -from typing import List, Dict +from typing import Dict, List import pytest from helpers import insert_benchmarking_data diff --git a/integration/benchmarks/helpers.py b/integration/benchmarks/helpers.py index 8708629500..f4e893b268 100644 --- a/integration/benchmarks/helpers.py +++ b/integration/benchmarks/helpers.py @@ -2,7 +2,7 @@ import json from datetime import datetime -from typing import List, Dict +from typing import Dict, List import psycopg2 diff --git a/integration/benchmarks/test_blank_app.py b/integration/benchmarks/test_blank_app.py index 9b556e7c68..7e7d552672 100644 --- a/integration/benchmarks/test_blank_app.py +++ b/integration/benchmarks/test_blank_app.py @@ -1,12 +1,12 @@ -import pytest +import os import time - from typing import Generator import pytest -from selenium.webdriver.common.by import By -from reflex.testing import DEFAULT_TIMEOUT, AppHarness, WebDriver, chdir +from reflex import constants +from reflex.testing import AppHarness, chdir +from reflex.utils import build, path_ops def BlankTemplate(): @@ -20,6 +20,7 @@ def BlankTemplate(): class State(rx.State): """The app state.""" + pass def index() -> rx.Component: @@ -44,6 +45,108 @@ def index() -> rx.Component: app.add_page(index) +def BlankTemplate2(): + """Test that background tasks work as expected.""" + from rxconfig import config + + 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) + + def index() -> rx.Component: + return 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", + ) + + def selection() -> rx.Component: + return rx.vstack( + rx.hstack( + rx.vstack( + rx.select( + ["C", "PF", "SF", "PG", "SG"], + placeholder="Select a position. (All)", + on_change=State.set_position, + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, + 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, + ), + 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, + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + app.add_page(index) + app.add_page(selection) + + @pytest.fixture(scope="session") def blank_template( tmp_path_factory, @@ -57,31 +160,60 @@ def blank_template( running AppHarness instance """ root = tmp_path_factory.mktemp(f"blank_template") - with AppHarness.create( - root=root, - app_source=BlankTemplate, # type: ignore - ) as harness: - yield harness + yield AppHarness.create(root=root, app_source=BlankTemplate) # type: ignore + + +@pytest.fixture(scope="session") +def blank_template_two_pages( + 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(f"blank_template_two_pages") + + yield AppHarness.create(root=root, app_source=BlankTemplate2) # type: ignore @pytest.mark.benchmark( - group="blank template", - min_time=0.1, - max_time=0.5, - min_rounds=10, - timer=time.perf_counter, - disable_gc=True, - warmup=False + group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False ) -def test_blank_template_app_start(benchmark, blank_template, mocker): - import copy - assert blank_template.app_instance is not None - from reflex.constants.base import Dirs - dirs = copy.deepcopy(Dirs) +def test_blank_template_compile_time(benchmark, blank_template): + def setup(): + with chdir(blank_template.app_path): + blank_template._initialize_app() + build.setup_frontend(blank_template.app_path) + + def benchmark_fn(): + with chdir(blank_template.app_path): + blank_template.app_instance.compile_() - dirs.WEB = str( blank_template.app_path /".web") - dirs.UTILS = "/".join([str( blank_template.app_path),"utils"]) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) - mocker.patch("reflex.utils.prerequisites.constants.Dirs", dirs) - benchmark(blank_template.app_instance.compile_) \ No newline at end of file + +@pytest.mark.benchmark( + group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False +) +def test_blank_template_two_pages_compile_time(benchmark, blank_template_two_pages): + def setup(): + with chdir(blank_template_two_pages.app_path): + blank_template_two_pages._initialize_app() + build.setup_frontend(blank_template_two_pages.app_path) + + def benchmark_fn(): + with chdir(blank_template_two_pages.app_path): + blank_template_two_pages.app_instance.compile_() + path_ops.rm( + os.path.join( + constants.Dirs.WEB, "reflex.install_frontend_packages.cached" + ) + ) + path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) + + benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) From 55827b3b6043d5e32dcb27a9a60c331b27d345f0 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 15:40:31 +0000 Subject: [PATCH 22/60] segregation --- .../{test_blank_app.py => test_base_app.py} | 83 ++++++--- integration/benchmarks/test_large_app.py | 0 .../benchmarks/test_medium_size_app.py | 166 ++++++++++++++++++ 3 files changed, 227 insertions(+), 22 deletions(-) rename integration/benchmarks/{test_blank_app.py => test_base_app.py} (72%) create mode 100644 integration/benchmarks/test_large_app.py create mode 100644 integration/benchmarks/test_medium_size_app.py diff --git a/integration/benchmarks/test_blank_app.py b/integration/benchmarks/test_base_app.py similarity index 72% rename from integration/benchmarks/test_blank_app.py rename to integration/benchmarks/test_base_app.py index 7e7d552672..2eb8aad753 100644 --- a/integration/benchmarks/test_blank_app.py +++ b/integration/benchmarks/test_base_app.py @@ -9,7 +9,7 @@ from reflex.utils import build, path_ops -def BlankTemplate(): +def BaseApp(): """Test that background tasks work as expected.""" from rxconfig import config @@ -45,7 +45,7 @@ def index() -> rx.Component: app.add_page(index) -def BlankTemplate2(): +def BaseApp2(): """Test that background tasks work as expected.""" from rxconfig import config @@ -148,10 +148,10 @@ def selection() -> rx.Component: @pytest.fixture(scope="session") -def blank_template( +def base_app( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Start Base app at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture @@ -159,16 +159,16 @@ def blank_template( Yields: running AppHarness instance """ - root = tmp_path_factory.mktemp(f"blank_template") + root = tmp_path_factory.mktemp(f"base_app") - yield AppHarness.create(root=root, app_source=BlankTemplate) # type: ignore + yield AppHarness.create(root=root, app_source=BaseApp) # type: ignore @pytest.fixture(scope="session") -def blank_template_two_pages( +def base_app_two_pages( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Start Base app at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture @@ -176,23 +176,23 @@ def blank_template_two_pages( Yields: running AppHarness instance """ - root = tmp_path_factory.mktemp(f"blank_template_two_pages") + root = tmp_path_factory.mktemp(f"base_app_two_pages") - yield AppHarness.create(root=root, app_source=BlankTemplate2) # type: ignore + yield AppHarness.create(root=root, app_source=BaseApp2) # type: ignore @pytest.mark.benchmark( group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False ) -def test_blank_template_compile_time(benchmark, blank_template): +def test_base_app_compile_time_cold(benchmark, base_app): def setup(): - with chdir(blank_template.app_path): - blank_template._initialize_app() - build.setup_frontend(blank_template.app_path) + with chdir(base_app.app_path): + base_app._initialize_app() + build.setup_frontend(base_app.app_path) def benchmark_fn(): - with chdir(blank_template.app_path): - blank_template.app_instance.compile_() + with chdir(base_app.app_path): + base_app.app_instance.compile_() benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) @@ -200,15 +200,15 @@ def benchmark_fn(): @pytest.mark.benchmark( group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False ) -def test_blank_template_two_pages_compile_time(benchmark, blank_template_two_pages): +def test_base_app_two_pages_compile_time_cold(benchmark, base_app_two_pages): def setup(): - with chdir(blank_template_two_pages.app_path): - blank_template_two_pages._initialize_app() - build.setup_frontend(blank_template_two_pages.app_path) + with chdir(base_app_two_pages.app_path): + base_app_two_pages._initialize_app() + build.setup_frontend(base_app_two_pages.app_path) def benchmark_fn(): - with chdir(blank_template_two_pages.app_path): - blank_template_two_pages.app_instance.compile_() + with chdir(base_app_two_pages.app_path): + base_app_two_pages.app_instance.compile_() path_ops.rm( os.path.join( constants.Dirs.WEB, "reflex.install_frontend_packages.cached" @@ -217,3 +217,42 @@ def benchmark_fn(): path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) + + +@pytest.mark.benchmark( + group="blank template", min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False +) +def test_base_app_compile_time_warm(benchmark, base_app): + + def benchmark_fn(): + with chdir(base_app.app_path): + base_app.app_instance.compile_() + + benchmark(benchmark_fn, rounds=10) + + +@pytest.mark.benchmark( + group="blank template", min_rounds=10,timer=time.perf_counter, disable_gc=True, warmup=False +) +def test_base_app_two_pages_compile_time_warm(benchmark, base_app_two_pages): + + def benchmark_fn(): + with chdir(base_app_two_pages.app_path): + base_app_two_pages.app_instance.compile_() + path_ops.rm( + os.path.join( + constants.Dirs.WEB, "reflex.install_frontend_packages.cached" + ) + ) + path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) + + benchmark(benchmark_fn) + + +def test_base_app_hot_reload(): + pass + + + +def test_base_app_two_pages_hot_reload(): + pass \ No newline at end of file diff --git a/integration/benchmarks/test_large_app.py b/integration/benchmarks/test_large_app.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration/benchmarks/test_medium_size_app.py b/integration/benchmarks/test_medium_size_app.py new file mode 100644 index 0000000000..549bfed63b --- /dev/null +++ b/integration/benchmarks/test_medium_size_app.py @@ -0,0 +1,166 @@ +import os +import time +from typing import Generator + +import pytest + +from reflex import constants +from reflex.testing import AppHarness, chdir +from reflex.utils import build, path_ops + + +def MediumApp(): + """Test that background tasks work as expected.""" + from rxconfig import config + + 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, + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, + 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, + ), + 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, + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + for i in range(1, 31): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") + + + +@pytest.fixture(scope="session") +def medium_app( + 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(f"medium_app") + + yield AppHarness.create(root=root, app_source=MediumApp) # type: ignore + + + +@pytest.mark.benchmark( + group="Medium sized app", min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False +) +def test_medium_app_compile_time_cold(benchmark, medium_app): + def setup(): + with chdir(medium_app.app_path): + medium_app._initialize_app() + build.setup_frontend(medium_app.app_path) + + def benchmark_fn(): + with chdir(medium_app.app_path): + medium_app.app_instance.compile_() + + benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) + + + +@pytest.mark.benchmark( + group="Medium sized app", min_rounds=10,timer=time.perf_counter, disable_gc=True, warmup=False +) +def test_medium_app_compile_time_warm(benchmark, medium_app): + + def benchmark_fn(): + with chdir(medium_app.app_path): + medium_app.app_instance.compile_() + + benchmark(benchmark_fn) + + + +def test_medium_app_hot_reload(): + pass From c7696ebad416233d9aff66575edf27d519cc0fb1 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 5 Mar 2024 15:43:21 +0000 Subject: [PATCH 23/60] remove codspeed.yml --- .github/workflows/benchmarks.yml | 40 +++++++++++++++++++++++++++++ .github/workflows/codspeed.yml | 43 -------------------------------- 2 files changed, 40 insertions(+), 43 deletions(-) delete mode 100644 .github/workflows/codspeed.yml diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 5b98280155..4b6a6932aa 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -67,3 +67,43 @@ jobs: poetry run bash scripts/benchmarks.sh ./reflex-web prod env: LHCI_GITHUB_APP_TOKEN: $ + + + reflex-benchmarks: + timeout-minutes: 30 + 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.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] + exclude: + - os: windows-latest + python-version: '3.10.13' + - os: windows-latest + python-version: '3.9.18' + - os: windows-latest + python-version: '3.8.18' + include: + - os: windows-latest + python-version: '3.10.11' + - os: windows-latest + python-version: '3.9.13' + - os: windows-latest + python-version: '3.8.10' + + 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 + - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow + - name: Run Benchmarks + # Only run if the database creds are available in this context. + uses: CodSpeedHQ/action@v2 + with: + run: poetry run pytest integration/benchmarks/ --codspeed diff --git a/.github/workflows/codspeed.yml b/.github/workflows/codspeed.yml deleted file mode 100644 index e7cc42019f..0000000000 --- a/.github/workflows/codspeed.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: codspeed-benchmarks - -on: - push: - branches: [main] - paths-ignore: - - '**/*.md' - pull_request: - branches: [main] - paths-ignore: - - '**/*.md' - -permissions: - contents: read - -defaults: - run: - shell: bash - -jobs: - benchmarks: - 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 ] - python-version: [ '3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] - - 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 - - - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow - - name: Run benchmarks - uses: CodSpeedHQ/action@v2 - with: - run: poetry run pytest integration/benchmarks/ --codspeed From bf721a6fd6988e79a2563e732891acf73ae9d9da Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 15:18:57 -0800 Subject: [PATCH 24/60] update --- .github/workflows/benchmarks.yml | 31 +- .pre-commit-config.yaml | 1 + integration/benchmarks/helpers.py | 50 -- .../{test_base_app.py => test_base_apps.py} | 35 +- .../{test_large_app.py => test_large_apps.py} | 0 integration/benchmarks/test_medium_apps.py | 169 +++++++ .../benchmarks/test_medium_size_app.py | 166 ------- poetry.lock | 465 +++++++++--------- pyproject.toml | 48 +- scripts/benchmark_helpers.py | 43 ++ .../reflex_web_benchmark_and_upload.py | 107 ++-- scripts/upload_simple_app_benchmarks.py | 103 ++++ 12 files changed, 679 insertions(+), 539 deletions(-) delete mode 100644 integration/benchmarks/helpers.py rename integration/benchmarks/{test_base_app.py => test_base_apps.py} (90%) rename integration/benchmarks/{test_large_app.py => test_large_apps.py} (100%) create mode 100644 integration/benchmarks/test_medium_apps.py delete mode 100644 integration/benchmarks/test_medium_size_app.py create mode 100644 scripts/benchmark_helpers.py rename integration/benchmarks/benchmarks.py => scripts/reflex_web_benchmark_and_upload.py (60%) create mode 100644 scripts/upload_simple_app_benchmarks.py diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 4b6a6932aa..dd85779acf 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -21,11 +21,10 @@ env: PYTHONIOENCODING: 'utf8' TELEMETRY_ENABLED: false NODE_OPTIONS: '--max_old_space_size=4096' + DATABASE_URL: ${{ secrets.DATABASE_URL }} jobs: reflex-web: - env: - DATABASE_URL: ${{ secrets.DATABASE_URL }} strategy: fail-fast: false matrix: @@ -67,17 +66,25 @@ jobs: poetry run bash scripts/benchmarks.sh ./reflex-web prod env: LHCI_GITHUB_APP_TOKEN: $ + - name: Run Benchmarks + # Only run if the database creds are available in this context. + if: ${{ env.DATABASE_URL }} + run: poetry run python scripts/reflex_web_benchmark_and_upload.py "$GITHUB_SHA" ./integration/benchmarks/.lighthouseci + env: + GITHUB_SHA: ${{ github.sha }} + PR_TITLE: ${{ github.event.pull_request.title }} - - reflex-benchmarks: + simple-apps-benchmarks: + env: + OUTPUT_FILE: benchmarks.json timeout-minutes: 30 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.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0' ] + os: [ubuntu-latest, windows-latest, macos-latest] + python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] exclude: - os: windows-latest python-version: '3.10.13' @@ -101,9 +108,11 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip install pytest-codspeed pyvirtualdisplay pillow - - name: Run Benchmarks + - name: Run benchmark tests + run: | + export PYTHONUNBUFFERED=1 + poetry run pytest -v integration/benchmarks/ --benchmark-json==${{ env.OUTPUT_FILE }} + - name: Upload benchmark results # Only run if the database creds are available in this context. - uses: CodSpeedHQ/action@v2 - with: - run: poetry run pytest integration/benchmarks/ --codspeed + if: ${{ env.DATABASE_URL }} + run: poetry run python scripts/upload_simple_app_benchmarks.py --os ${{ matrix.os }} --python-version ${{ matrix.python-version }} --commit-sha ${{ github.sha }} --benchmarks-file ${{ env.OUTPUT_FILE }} --pr-title ${{ github.event.pull_request.title }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3380683c93..13951736f7 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,7 @@ repos: hooks: - id: ruff args: [--fix, --exit-non-zero-on-fix] + exclude: '^integration/benchmarks/' - repo: https://github.com/RobertCraigie/pyright-python rev: v1.1.313 diff --git a/integration/benchmarks/helpers.py b/integration/benchmarks/helpers.py deleted file mode 100644 index f4e893b268..0000000000 --- a/integration/benchmarks/helpers.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Helper functions for the benchmarking integration.""" - -import json -from datetime import datetime -from typing import Dict, List - -import psycopg2 - - -def insert_benchmarking_data( - db_connection_url: str, - lighthouse_data: Dict, - performance_data: List[Dict], - commit_sha: str, - pr_title: str, -): - """Insert the benchmarking data into the database. - - Args: - db_connection_url: The URL to connect to the database. - lighthouse_data: The Lighthouse data to insert. - performance_data: The performance data to insert. - commit_sha: The commit SHA to insert. - pr_title: The PR title to insert. - """ - # Serialize the JSON data - lighthouse_json = json.dumps(lighthouse_data) - performance_json = json.dumps(performance_data) - - # Get the current timestamp - current_timestamp = datetime.now() - - # Connect to the database and insert the data - with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: - insert_query = """ - INSERT INTO benchmarks (lighthouse, performance, commit_sha, pr_title, time) - VALUES (%s, %s, %s, %s, %s); - """ - cursor.execute( - insert_query, - ( - lighthouse_json, - performance_json, - commit_sha, - pr_title, - current_timestamp, - ), - ) - # Commit the transaction - conn.commit() diff --git a/integration/benchmarks/test_base_app.py b/integration/benchmarks/test_base_apps.py similarity index 90% rename from integration/benchmarks/test_base_app.py rename to integration/benchmarks/test_base_apps.py index 2eb8aad753..5e84ca99e6 100644 --- a/integration/benchmarks/test_base_app.py +++ b/integration/benchmarks/test_base_apps.py @@ -1,3 +1,5 @@ +"""Benchmark tests for base apps.""" + import os import time from typing import Generator @@ -11,7 +13,7 @@ def BaseApp(): """Test that background tasks work as expected.""" - from rxconfig import config + from rxconfig import config # type: ignore import reflex as rx @@ -47,7 +49,7 @@ def index() -> rx.Component: def BaseApp2(): """Test that background tasks work as expected.""" - from rxconfig import config + from rxconfig import config # type: ignore import reflex as rx @@ -95,13 +97,13 @@ def selection() -> rx.Component: rx.select( ["C", "PF", "SF", "PG", "SG"], placeholder="Select a position. (All)", - on_change=State.set_position, + on_change=State.set_position, # type: ignore size="3", ), rx.select( college, placeholder="Select a college. (All)", - on_change=State.set_college, + on_change=State.set_college, # type: ignore size="3", ), ), @@ -116,7 +118,7 @@ def selection() -> rx.Component: default_value=[18, 50], min=18, max=50, - on_value_commit=State.set_age, + on_value_commit=State.set_age, # type: ignore ), align_items="left", width="100%", @@ -131,7 +133,7 @@ def selection() -> rx.Component: default_value=[0, 25000000], min=0, max=25000000, - on_value_commit=State.set_salary, + on_value_commit=State.set_salary, # type: ignore ), align_items="left", width="100%", @@ -220,22 +222,28 @@ def benchmark_fn(): @pytest.mark.benchmark( - group="blank template", min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False + group="blank template", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, ) def test_base_app_compile_time_warm(benchmark, base_app): - def benchmark_fn(): with chdir(base_app.app_path): base_app.app_instance.compile_() - benchmark(benchmark_fn, rounds=10) + benchmark.pedantic(benchmark_fn, rounds=10) @pytest.mark.benchmark( - group="blank template", min_rounds=10,timer=time.perf_counter, disable_gc=True, warmup=False + group="blank template", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, ) def test_base_app_two_pages_compile_time_warm(benchmark, base_app_two_pages): - def benchmark_fn(): with chdir(base_app_two_pages.app_path): base_app_two_pages.app_instance.compile_() @@ -246,13 +254,12 @@ def benchmark_fn(): ) path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) - benchmark(benchmark_fn) + benchmark.pedantic(benchmark_fn) def test_base_app_hot_reload(): pass - def test_base_app_two_pages_hot_reload(): - pass \ No newline at end of file + pass diff --git a/integration/benchmarks/test_large_app.py b/integration/benchmarks/test_large_apps.py similarity index 100% rename from integration/benchmarks/test_large_app.py rename to integration/benchmarks/test_large_apps.py diff --git a/integration/benchmarks/test_medium_apps.py b/integration/benchmarks/test_medium_apps.py new file mode 100644 index 0000000000..ab139cb95e --- /dev/null +++ b/integration/benchmarks/test_medium_apps.py @@ -0,0 +1,169 @@ +"""Benchmark tests for medium sized apps.""" + +import time +from typing import Generator + +import pytest + +from reflex.testing import AppHarness, chdir +from reflex.utils import build + + +def MediumApp(): + """Test that background tasks work as expected.""" + from rxconfig import config # type: ignore + + 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, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + 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, # type: ignore + ), + 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, # type: ignore + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + for i in range(1, 31): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") + + +@pytest.fixture(scope="session") +def medium_app( + 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(f"medium_app") + + yield AppHarness.create(root=root, app_source=MediumApp) # type: ignore + + +@pytest.mark.benchmark( + group="Medium sized app", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_medium_app_compile_time_cold(benchmark, medium_app): + def setup(): + with chdir(medium_app.app_path): + medium_app._initialize_app() + build.setup_frontend(medium_app.app_path) + + def benchmark_fn(): + with chdir(medium_app.app_path): + medium_app.app_instance.compile_() + + benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) + + +@pytest.mark.benchmark( + group="Medium sized app", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_medium_app_compile_time_warm(benchmark, medium_app): + def benchmark_fn(): + with chdir(medium_app.app_path): + medium_app.app_instance.compile_() + + benchmark(benchmark_fn) + + +def test_medium_app_hot_reload(): + pass diff --git a/integration/benchmarks/test_medium_size_app.py b/integration/benchmarks/test_medium_size_app.py deleted file mode 100644 index 549bfed63b..0000000000 --- a/integration/benchmarks/test_medium_size_app.py +++ /dev/null @@ -1,166 +0,0 @@ -import os -import time -from typing import Generator - -import pytest - -from reflex import constants -from reflex.testing import AppHarness, chdir -from reflex.utils import build, path_ops - - -def MediumApp(): - """Test that background tasks work as expected.""" - from rxconfig import config - - 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, - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, - 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, - ), - 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, - ), - align_items="left", - width="100%", - ), - ), - spacing="4", - ), - width="100%", - ) - - app = rx.App(state=rx.State) - - for i in range(1, 31): - if i % 2 == 1: - app.add_page(comp1, route=f"page{i}") - else: - app.add_page(comp2, route=f"page{i}") - - - -@pytest.fixture(scope="session") -def medium_app( - 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(f"medium_app") - - yield AppHarness.create(root=root, app_source=MediumApp) # type: ignore - - - -@pytest.mark.benchmark( - group="Medium sized app", min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False -) -def test_medium_app_compile_time_cold(benchmark, medium_app): - def setup(): - with chdir(medium_app.app_path): - medium_app._initialize_app() - build.setup_frontend(medium_app.app_path) - - def benchmark_fn(): - with chdir(medium_app.app_path): - medium_app.app_instance.compile_() - - benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) - - - -@pytest.mark.benchmark( - group="Medium sized app", min_rounds=10,timer=time.perf_counter, disable_gc=True, warmup=False -) -def test_medium_app_compile_time_warm(benchmark, medium_app): - - def benchmark_fn(): - with chdir(medium_app.app_path): - medium_app.app_instance.compile_() - - benchmark(benchmark_fn) - - - -def test_medium_app_hot_reload(): - pass diff --git a/poetry.lock b/poetry.lock index c676f8dc4e..b021d4c9fc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -132,18 +132,18 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "build" -version = "1.0.3" +version = "1.1.1" description = "A simple, correct Python build frontend" optional = false python-versions = ">= 3.7" files = [ - {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"}, - {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"}, + {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"}, + {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} packaging = ">=19.0" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -167,63 +167,75 @@ files = [ [[package]] name = "cffi" -version = "1.16.0" +version = "1.15.1" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = ">=3.8" +python-versions = "*" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, + {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, + {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, + {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, + {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, + {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, + {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, + {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, + {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, + {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, + {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, + {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, + {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, + {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, + {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, + {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, + {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, + {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, + {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, + {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, + {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, + {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, + {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, + {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, + {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, + {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, + {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, + {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, + {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, + {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, + {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, + {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, + {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] [package.dependencies] @@ -377,63 +389,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.1" +version = "7.4.3" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, - {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, - {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, - {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, - {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, - {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, - {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, - {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, - {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, - {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, - {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, - {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, - {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, - {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, - {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, - {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, - {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, - {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, - {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, - {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, - {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, - {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, - {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, - {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, - {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, - {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, - {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, - {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, - {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, - {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, - {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, - {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] [package.dependencies] @@ -522,19 +534,19 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6 [[package]] name = "filelock" -version = "3.13.1" +version = "3.12.4" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, - {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, + {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, + {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, ] [package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] +docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] +typing = ["typing-extensions (>=4.7.1)"] [[package]] name = "greenlet" @@ -640,13 +652,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.3" +version = "1.0.4" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.3-py3-none-any.whl", hash = "sha256:9a6a501c3099307d9fd76ac244e08503427679b1e81ceb1d922485e2f2462ad2"}, - {file = "httpcore-1.0.3.tar.gz", hash = "sha256:5c0f9546ad17dac4d0772b0808856eb616eb8b48ce94f49ed819fd6982a8a544"}, + {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"}, + {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"}, ] [package.dependencies] @@ -657,7 +669,7 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.24.0)"] +trio = ["trio (>=0.22.0,<0.25.0)"] [[package]] name = "httpx" @@ -729,13 +741,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.1.1" +version = "6.1.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, - {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, + {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, + {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, ] [package.dependencies] @@ -743,7 +755,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -1074,40 +1086,40 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] [[package]] name = "pandas" -version = "2.2.0" +version = "2.2.1" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, - {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, - {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, - {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, - {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, - {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, - {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, - {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, - {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, - {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, - {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, - {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, - {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, - {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, - {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, - {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, - {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, - {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, + {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, + {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, + {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, + {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, + {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, + {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, + {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, + {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, + {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, + {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, + {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, + {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, + {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, + {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, + {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, + {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, + {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, ] [package.dependencies] @@ -1139,6 +1151,7 @@ parquet = ["pyarrow (>=10.0.1)"] performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] plot = ["matplotlib (>=3.6.3)"] postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] +pyarrow = ["pyarrow (>=10.0.1)"] spss = ["pyreadstat (>=1.2.0)"] sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] @@ -1242,13 +1255,13 @@ xmp = ["defusedxml"] [[package]] name = "pipdeptree" -version = "2.15.1" +version = "2.16.1" description = "Command line utility to show dependency tree of packages." optional = false python-versions = ">=3.8" files = [ - {file = "pipdeptree-2.15.1-py3-none-any.whl", hash = "sha256:0abe6b5b9f86a79ea023a633d7526de14b64b848bbdf980c610d08d679c8329d"}, - {file = "pipdeptree-2.15.1.tar.gz", hash = "sha256:e4ae44fc9d0c747125316981804d7786e9d97bf59c128249b8ea272a7cbdecc4"}, + {file = "pipdeptree-2.16.1-py3-none-any.whl", hash = "sha256:7131023df30220678b6a42c7372ce07453b39e0b3df935b129e832f0acf96b8a"}, + {file = "pipdeptree-2.16.1.tar.gz", hash = "sha256:f1ca64ce4aff9373613711048b9c4e8106ad955dea0dd962b7974fa168d7650a"}, ] [package.extras] @@ -1591,13 +1604,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "python-dateutil" -version = "2.8.2" +version = "2.9.0.post0" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, - {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] [package.dependencies] @@ -1780,13 +1793,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.0" +version = "13.7.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, - {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, ] [package.dependencies] @@ -1824,13 +1837,13 @@ files = [ [[package]] name = "selenium" -version = "4.18.0" +version = "4.18.1" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "selenium-4.18.0-py3-none-any.whl", hash = "sha256:d0ac1ab0aa50dffad8050e7f0170395ea23bcecccf7d59b69bc430e47eeb9dac"}, - {file = "selenium-4.18.0.tar.gz", hash = "sha256:8912e690d4e72350ca733ad28be894e87683064de3304e5f5b5b386697a6c47d"}, + {file = "selenium-4.18.1-py3-none-any.whl", hash = "sha256:b24a3cdd2d47c29832e81345bfcde0c12bb608738013e53c781b211b418df241"}, + {file = "selenium-4.18.1.tar.gz", hash = "sha256:a11f67afa8bfac6b77e148c987b33f6b14eb1cae4d352722a75de1f26e3f0ae2"}, ] [package.dependencies] @@ -1886,13 +1899,13 @@ files = [ [[package]] name = "sniffio" -version = "1.3.0" +version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, - {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, + {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, + {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] [[package]] @@ -1908,60 +1921,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.27" +version = "2.0.28" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, - {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, - {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, - {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, - {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, - {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, - {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, - {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, - {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, + {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, + {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, + {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, + {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, + {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, + {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, + {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, + {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, ] [package.dependencies] @@ -2168,13 +2181,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.9.0" +version = "4.10.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, - {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, + {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, + {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, ] [[package]] @@ -2246,13 +2259,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.25.0" +version = "20.25.1" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, - {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, + {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, + {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 61f67bfb70..b177e64ee1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,16 +14,9 @@ readme = "README.md" homepage = "https://reflex.dev" repository = "https://github.com/reflex-dev/reflex" documentation = "https://reflex.dev/docs/getting-started/introduction" -keywords = [ - "web", - "framework", -] -classifiers = [ - "Development Status :: 4 - Beta", -] -packages = [ - {include = "reflex"} -] +keywords = ["web", "framework"] +classifiers = ["Development Status :: 4 - Beta"] +packages = [{ include = "reflex" }] [tool.poetry.dependencies] python = "^3.8" @@ -41,19 +34,19 @@ rich = "^13.0.0" sqlmodel = "^0.0.14" typer = ">=0.4.2,<1" uvicorn = [ - {version = "^0.24.0", python = ">=3.12"}, - {version = "^0.20.0", python = "<3.12"}, + { version = "^0.24.0", python = ">=3.12" }, + { version = "^0.20.0", python = "<3.12" }, ] watchdog = "^2.3.1" watchfiles = "^0.19.0" starlette-admin = "^0.9.0" alembic = "^1.11.1" platformdirs = "^3.10.0" -distro = {version = "^1.8.0", platform = "linux"} +distro = { version = "^1.8.0", platform = "linux" } python-engineio = "!=4.6.0" wrapt = [ - {version = "^1.14.0", python = ">=3.11"}, - {version = "^1.11.0", python = "<3.11"}, + { version = "^1.14.0", python = ">=3.11" }, + { version = "^1.11.0", python = "<3.11" }, ] packaging = "^23.1" reflex-hosting-cli = ">=0.1.2" @@ -73,19 +66,16 @@ pytest-cov = "^4.0.0" black = "^22.10.0" ruff = "^0.0.244" pandas = [ - {version = "^2.1.1", python = ">=3.9,<3.13"}, - {version = "^1.5.3", python = ">=3.8,<3.9"}, -] -pillow = [ - {version = "^10.0.0", python = ">=3.8,<4.0"} + { version = "^2.1.1", python = ">=3.9,<3.13" }, + { version = "^1.5.3", python = ">=3.8,<3.9" }, ] +pillow = [{ version = "^10.0.0", python = ">=3.8,<4.0" }] plotly = "^5.13.0" asynctest = "^0.13.0" -pre-commit = {version = "^3.2.1", python = ">=3.8,<4.0"} +pre-commit = { version = "^3.2.1", python = ">=3.8,<4.0" } selenium = "^4.11.0" types-tabulate = "^0.9.0.3" pytest-benchmark = "^4.0.0" -pytest-codspeed = "^2.2.0" [tool.poetry.scripts] reflex = "reflex.reflex:cli" @@ -100,7 +90,19 @@ build-backend = "poetry.core.masonry.api" select = ["B", "D", "E", "F", "I", "SIM", "W"] -ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"] +ignore = [ + "B008", + "D203", + "D205", + "D213", + "D401", + "D406", + "D407", + "E501", + "F403", + "F405", + "F541", +] target-version = "py37" diff --git a/scripts/benchmark_helpers.py b/scripts/benchmark_helpers.py new file mode 100644 index 0000000000..3db0af5fd0 --- /dev/null +++ b/scripts/benchmark_helpers.py @@ -0,0 +1,43 @@ +"""Helper functions for the benchmarking scripts.""" + +import json + + +def extract_stats_from_json(json_file: str) -> list[dict]: + """Extracts the stats from the JSON data and returns them as a list of dictionaries. + + Args: + json_file: The JSON file to extract the stats data from. + + Returns: + list[dict]: The stats for each test. + """ + with open(json_file, "r") as file: + json_data = json.load(file) + + # Load the JSON data if it is a string, otherwise assume it's already a dictionary + data = json.loads(json_data) if isinstance(json_data, str) else json_data + + # Initialize an empty list to store the stats for each test + test_stats = [] + + # Iterate over each test in the 'benchmarks' list + for test in data.get("benchmarks", []): + stats = test.get("stats", {}) + test_name = test.get("name", "Unknown Test") + min_value = stats.get("min", None) + max_value = stats.get("max", None) + mean_value = stats.get("mean", None) + stdev_value = stats.get("stddev", None) + + test_stats.append( + { + "test_name": test_name, + "min": min_value, + "max": max_value, + "mean": mean_value, + "stdev": stdev_value, + } + ) + + return test_stats diff --git a/integration/benchmarks/benchmarks.py b/scripts/reflex_web_benchmark_and_upload.py similarity index 60% rename from integration/benchmarks/benchmarks.py rename to scripts/reflex_web_benchmark_and_upload.py index 6a9b67f072..b3cd12d0d1 100644 --- a/integration/benchmarks/benchmarks.py +++ b/scripts/reflex_web_benchmark_and_upload.py @@ -1,15 +1,61 @@ """Runs the benchmarks and inserts the results into the database.""" +from __future__ import annotations + import json import os import sys -from typing import Dict, List +from datetime import datetime +import psycopg2 import pytest -from helpers import insert_benchmarking_data +from benchmark_helpers import extract_stats_from_json + + +def insert_benchmarking_data( + db_connection_url: str, + lighthouse_data: dict, + performance_data: list[dict], + commit_sha: str, + pr_title: str, +): + """Insert the benchmarking data into the database. + + Args: + db_connection_url: The URL to connect to the database. + lighthouse_data: The Lighthouse data to insert. + performance_data: The performance data to insert. + commit_sha: The commit SHA to insert. + pr_title: The PR title to insert. + """ + # Serialize the JSON data + lighthouse_json = json.dumps(lighthouse_data) + performance_json = json.dumps(performance_data) + + # Get the current timestamp + current_timestamp = datetime.now() + + # Connect to the database and insert the data + with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: + insert_query = """ + INSERT INTO benchmarks (lighthouse, performance, commit_sha, pr_title, time) + VALUES (%s, %s, %s, %s, %s); + """ + cursor.execute( + insert_query, + ( + lighthouse_json, + performance_json, + commit_sha, + pr_title, + current_timestamp, + ), + ) + # Commit the transaction + conn.commit() -def get_lighthouse_scores(directory_path: str) -> Dict: +def get_lighthouse_scores(directory_path: str) -> dict: """Extracts the Lighthouse scores from the JSON files in the specified directory. Args: @@ -45,20 +91,23 @@ def get_lighthouse_scores(directory_path: str) -> Dict: return scores -def run_pytest_and_get_results(test_path=None) -> List: +def run_pytest_and_get_results(test_path=None) -> str: """Runs pytest and returns the results. Args: test_path: The path to the tests to run. Returns: - dict: The results of the tests. + the name of the output file """ # Set the default path to the current directory if no path is provided if not test_path: test_path = os.getcwd() + + output_file = "benchmark_report.json" + # Ensure you have installed the pytest-json plugin before running this - pytest_args = ["-v", "--benchmark-json=benchmark_report.json", test_path] + pytest_args = ["-v", f"--benchmark-json={output_file}", test_path] # Run pytest with the specified arguments pytest.main(pytest_args) @@ -66,47 +115,7 @@ def run_pytest_and_get_results(test_path=None) -> List: # Print ls of the current directory print(os.listdir()) - with open("benchmark_report.json", "r") as file: - pytest_results = json.load(file) - - return pytest_results - - -def extract_stats_from_json(json_data) -> List[Dict]: - """Extracts the stats from the JSON data and returns them as a list of dictionaries. - - Args: - json_data: The JSON data to extract the stats from. - - Returns: - list[dict]: The stats for each test. - """ - # Load the JSON data if it is a string, otherwise assume it's already a dictionary - data = json.loads(json_data) if isinstance(json_data, str) else json_data - - # Initialize an empty list to store the stats for each test - test_stats = [] - - # Iterate over each test in the 'benchmarks' list - for test in data.get("benchmarks", []): - stats = test.get("stats", {}) - test_name = test.get("name", "Unknown Test") - min_value = stats.get("min", None) - max_value = stats.get("max", None) - mean_value = stats.get("mean", None) - stdev_value = stats.get("stddev", None) - - test_stats.append( - { - "test_name": test_name, - "min": min_value, - "max": max_value, - "mean": mean_value, - "stdev": stdev_value, - } - ) - - return test_stats + return output_file def main(): @@ -123,8 +132,8 @@ def main(): sys.exit("Missing environment variables") # Run pytest and get the results - results = run_pytest_and_get_results() - cleaned_results = extract_stats_from_json(results) + result_json_file = run_pytest_and_get_results() + cleaned_results = extract_stats_from_json(result_json_file) # Get the Lighthouse scores lighthouse_scores = get_lighthouse_scores(json_dir) diff --git a/scripts/upload_simple_app_benchmarks.py b/scripts/upload_simple_app_benchmarks.py new file mode 100644 index 0000000000..75219b8c1d --- /dev/null +++ b/scripts/upload_simple_app_benchmarks.py @@ -0,0 +1,103 @@ +"""Runs the benchmarks and inserts the results into the database.""" + +from __future__ import annotations + +import argparse +import json +import os +import sys +from datetime import datetime + +import psycopg2 +from benchmark_helpers import extract_stats_from_json + + +def insert_benchmarking_data( + db_connection_url: str, + os_type_version: str, + python_version: str, + performance_data: list[dict], + commit_sha: str, + pr_title: str, +): + """Insert the benchmarking data into the database. + + Args: + db_connection_url: The URL to connect to the database. + os_type_version: The OS type and version to insert. + python_version: The Python version to insert. + performance_data: The performance data of reflex web to insert. + commit_sha: The commit SHA to insert. + pr_title: The PR title to insert. + """ + # Serialize the JSON data + simple_app_performance_json = json.dumps(performance_data) + + # Get the current timestamp + current_timestamp = datetime.now() + + # Connect to the database and insert the data + with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: + insert_query = """ + INSERT INTO simple_app_benchmarks (os, python_version, commit_sha, time, pr_title, performance) + VALUES (%s, %s, %s, %s, %s); + """ + cursor.execute( + insert_query, + ( + os_type_version, + python_version, + commit_sha, + current_timestamp, + pr_title, + simple_app_performance_json, + ), + ) + # Commit the transaction + conn.commit() + + +def main(): + """Runs the benchmarks and inserts the results.""" + # Get the commit SHA and JSON directory from the command line arguments + parser = argparse.ArgumentParser(description="Run benchmarks and process results.") + parser.add_argument( + "--os", help="The OS type and version to insert into the database." + ) + parser.add_argument( + "--python-version", help="The Python version to insert into the database." + ) + parser.add_argument( + "--commit-sha", help="The commit SHA to insert into the database." + ) + parser.add_argument( + "--benchmark-json", + help="The JSON file containing the benchmark results.", + ) + parser.add_argument( + "--pr-title", + help="The PR title to insert into the database.", + ) + args = parser.parse_args() + + # Get the PR title and database URL from the environment variables + db_url = os.environ.get("DATABASE_URL") + + if db_url is None or args.pr_title is None: + sys.exit("Missing environment variables") + + # Get the results of pytest benchmarks + cleaned_benchmark_results = extract_stats_from_json(args.reflex_web_benchmark_json) + # Insert the data into the database + insert_benchmarking_data( + db_connection_url=db_url, + os_type_version=args.os, + python_version=args.python_version, + performance_data=cleaned_benchmark_results, + commit_sha=args.commit_sha, + pr_title=args.pr_title, + ) + + +if __name__ == "__main__": + main() From c7f23cb56ecd0ad4b223ff0b4b7682397ee39750 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:29:31 -0800 Subject: [PATCH 25/60] fix CI --- .github/workflows/benchmarks.yml | 1 + .github/workflows/integration_app_harness.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index dd85779acf..3c9ec38f9f 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -108,6 +108,7 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv + - run: poetry run pip install pyvirtualdisplay pillow - name: Run benchmark tests run: | export PYTHONUNBUFFERED=1 diff --git a/.github/workflows/integration_app_harness.yml b/.github/workflows/integration_app_harness.yml index ed74ac39db..f3aaf4f54d 100644 --- a/.github/workflows/integration_app_harness.yml +++ b/.github/workflows/integration_app_harness.yml @@ -47,7 +47,7 @@ jobs: SCREENSHOT_DIR: /tmp/screenshots REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} run: | - poetry run pytest integration + poetry run pytest integration --ignore=integration/benchmarks/ - uses: actions/upload-artifact@v4 name: Upload failed test screenshots if: always() From bbb1adbdc75355c38900bc7210006d67b9c7999d Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:36:29 -0800 Subject: [PATCH 26/60] comment, remove empty file --- integration/benchmarks/test_base_apps.py | 2 ++ integration/benchmarks/test_large_apps.py | 0 integration/benchmarks/test_medium_apps.py | 1 + 3 files changed, 3 insertions(+) delete mode 100644 integration/benchmarks/test_large_apps.py diff --git a/integration/benchmarks/test_base_apps.py b/integration/benchmarks/test_base_apps.py index 5e84ca99e6..c66e130dc1 100644 --- a/integration/benchmarks/test_base_apps.py +++ b/integration/benchmarks/test_base_apps.py @@ -257,9 +257,11 @@ def benchmark_fn(): benchmark.pedantic(benchmark_fn) +# TODO: def test_base_app_hot_reload(): pass +# TODO: def test_base_app_two_pages_hot_reload(): pass diff --git a/integration/benchmarks/test_large_apps.py b/integration/benchmarks/test_large_apps.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/integration/benchmarks/test_medium_apps.py b/integration/benchmarks/test_medium_apps.py index ab139cb95e..2a7d9b6912 100644 --- a/integration/benchmarks/test_medium_apps.py +++ b/integration/benchmarks/test_medium_apps.py @@ -165,5 +165,6 @@ def benchmark_fn(): benchmark(benchmark_fn) +# TODO: def test_medium_app_hot_reload(): pass From 0c286fc40deaccee03846d61a0672b1bd1997fd5 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:39:28 -0800 Subject: [PATCH 27/60] fix dep in CI again --- .github/workflows/benchmarks.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 3c9ec38f9f..3026cff84b 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -108,7 +108,8 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - run: poetry run pip install pyvirtualdisplay pillow + - name: Install additional dependencies for appharness test and DB + run: poetry run pip install pyvirtualdisplay pillow psycopg2-binary - name: Run benchmark tests run: | export PYTHONUNBUFFERED=1 From 3083b089f618b5e576ba87e78efbf09b01a551da Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:45:38 -0800 Subject: [PATCH 28/60] quotes around args --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 3026cff84b..9d9d763ea6 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -117,4 +117,4 @@ jobs: - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/upload_simple_app_benchmarks.py --os ${{ matrix.os }} --python-version ${{ matrix.python-version }} --commit-sha ${{ github.sha }} --benchmarks-file ${{ env.OUTPUT_FILE }} --pr-title ${{ github.event.pull_request.title }} + run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmarks-file "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" From dab5db921ceaa339d9ee034786befce0e2970444 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:47:17 -0800 Subject: [PATCH 29/60] fix args passing --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 9d9d763ea6..c1278b5725 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -117,4 +117,4 @@ jobs: - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmarks-file "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" + run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" From ed3121432db164c71b0baf6309945ab2e4afa0ba Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 16:58:10 -0800 Subject: [PATCH 30/60] fix type anno --- integration/benchmarks/test_base_apps.py | 2 ++ integration/benchmarks/test_medium_apps.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/integration/benchmarks/test_base_apps.py b/integration/benchmarks/test_base_apps.py index c66e130dc1..135d5dbb27 100644 --- a/integration/benchmarks/test_base_apps.py +++ b/integration/benchmarks/test_base_apps.py @@ -1,5 +1,7 @@ """Benchmark tests for base apps.""" +from __future__ import annotations + import os import time from typing import Generator diff --git a/integration/benchmarks/test_medium_apps.py b/integration/benchmarks/test_medium_apps.py index 2a7d9b6912..a571f8372a 100644 --- a/integration/benchmarks/test_medium_apps.py +++ b/integration/benchmarks/test_medium_apps.py @@ -1,5 +1,7 @@ """Benchmark tests for medium sized apps.""" +from __future__ import annotations + import time from typing import Generator From a5a0716fc3def334636a52dabd3669a1f69d1c48 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 17:03:47 -0800 Subject: [PATCH 31/60] fix args again --- scripts/upload_simple_app_benchmarks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upload_simple_app_benchmarks.py b/scripts/upload_simple_app_benchmarks.py index 75219b8c1d..1f3ed59d9b 100644 --- a/scripts/upload_simple_app_benchmarks.py +++ b/scripts/upload_simple_app_benchmarks.py @@ -87,7 +87,7 @@ def main(): sys.exit("Missing environment variables") # Get the results of pytest benchmarks - cleaned_benchmark_results = extract_stats_from_json(args.reflex_web_benchmark_json) + cleaned_benchmark_results = extract_stats_from_json(args.benchmark_json) # Insert the data into the database insert_benchmarking_data( db_connection_url=db_url, From c188dc530e25356fcffc8a167dae4918ca3dae3b Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:16:39 -0800 Subject: [PATCH 32/60] try fixing CI again --- .github/workflows/benchmarks.yml | 12 +++++++----- integration/benchmarks/test_base_apps.py | 7 +++---- integration/benchmarks/test_medium_apps.py | 5 +++-- scripts/benchmark_helpers.py | 1 - scripts/upload_simple_app_benchmarks.py | 16 +++++++--------- 5 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index c1278b5725..6ef18573cc 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -110,11 +110,13 @@ jobs: create-venv-at-path: .venv - name: Install additional dependencies for appharness test and DB run: poetry run pip install pyvirtualdisplay pillow psycopg2-binary - - name: Run benchmark tests + - name: Run benchmark tests and upload results run: | export PYTHONUNBUFFERED=1 poetry run pytest -v integration/benchmarks/ --benchmark-json==${{ env.OUTPUT_FILE }} - - name: Upload benchmark results - # Only run if the database creds are available in this context. - if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" + ls + if [ "${{ env.DATABASE_URL }}" != "" ]; then + poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" + else + echo "Skipping upload of benchmark results as DATABASE_URL is not available" + fi diff --git a/integration/benchmarks/test_base_apps.py b/integration/benchmarks/test_base_apps.py index 135d5dbb27..4ac522ecb5 100644 --- a/integration/benchmarks/test_base_apps.py +++ b/integration/benchmarks/test_base_apps.py @@ -1,7 +1,5 @@ """Benchmark tests for base apps.""" -from __future__ import annotations - import os import time from typing import Generator @@ -52,6 +50,7 @@ def index() -> rx.Component: def BaseApp2(): """Test that background tasks work as expected.""" from rxconfig import config # type: ignore + from typing import Tuple import reflex as rx @@ -71,8 +70,8 @@ class State(rx.State): position: str college: str - age: tuple[int, int] = (18, 50) - salary: tuple[int, int] = (0, 25000000) + age: Tuple[int, int] = (18, 50) + salary: Tuple[int, int] = (0, 25000000) def index() -> rx.Component: return rx.center( diff --git a/integration/benchmarks/test_medium_apps.py b/integration/benchmarks/test_medium_apps.py index a571f8372a..8798680995 100644 --- a/integration/benchmarks/test_medium_apps.py +++ b/integration/benchmarks/test_medium_apps.py @@ -14,6 +14,7 @@ def MediumApp(): """Test that background tasks work as expected.""" from rxconfig import config # type: ignore + from typing import Tuple import reflex as rx @@ -33,8 +34,8 @@ class State(rx.State): position: str college: str - age: tuple[int, int] = (18, 50) - salary: tuple[int, int] = (0, 25000000) + age: Tuple[int, int] = (18, 50) + salary: Tuple[int, int] = (0, 25000000) comp1 = rx.center( rx.theme_panel(), diff --git a/scripts/benchmark_helpers.py b/scripts/benchmark_helpers.py index 3db0af5fd0..bdf1f0107c 100644 --- a/scripts/benchmark_helpers.py +++ b/scripts/benchmark_helpers.py @@ -39,5 +39,4 @@ def extract_stats_from_json(json_file: str) -> list[dict]: "stdev": stdev_value, } ) - return test_stats diff --git a/scripts/upload_simple_app_benchmarks.py b/scripts/upload_simple_app_benchmarks.py index 1f3ed59d9b..080d027e3c 100644 --- a/scripts/upload_simple_app_benchmarks.py +++ b/scripts/upload_simple_app_benchmarks.py @@ -4,8 +4,6 @@ import argparse import json -import os -import sys from datetime import datetime import psycopg2 @@ -74,23 +72,23 @@ def main(): "--benchmark-json", help="The JSON file containing the benchmark results.", ) + parser.add_argument( + "--db-url", + help="The URL to connect to the database.", + required=True, + ) parser.add_argument( "--pr-title", help="The PR title to insert into the database.", + required=True, ) args = parser.parse_args() - # Get the PR title and database URL from the environment variables - db_url = os.environ.get("DATABASE_URL") - - if db_url is None or args.pr_title is None: - sys.exit("Missing environment variables") - # Get the results of pytest benchmarks cleaned_benchmark_results = extract_stats_from_json(args.benchmark_json) # Insert the data into the database insert_benchmarking_data( - db_connection_url=db_url, + db_connection_url=args.db_url, os_type_version=args.os, python_version=args.python_version, performance_data=cleaned_benchmark_results, From 8eff696c24e0e03cbeeed1a7271543597177c155 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:24:15 -0800 Subject: [PATCH 33/60] oops --- .github/workflows/benchmarks.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 6ef18573cc..f9fb13545f 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -110,13 +110,11 @@ jobs: create-venv-at-path: .venv - name: Install additional dependencies for appharness test and DB run: poetry run pip install pyvirtualdisplay pillow psycopg2-binary - - name: Run benchmark tests and upload results + - name: Run benchmark tests run: | export PYTHONUNBUFFERED=1 - poetry run pytest -v integration/benchmarks/ --benchmark-json==${{ env.OUTPUT_FILE }} - ls - if [ "${{ env.DATABASE_URL }}" != "" ]; then - poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" - else - echo "Skipping upload of benchmark results as DATABASE_URL is not available" - fi + poetry run pytest -v integration/benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} + - name: Upload benchmark results + # Only run if the database creds are available in this context. + if: ${{ env.DATABASE_URL }} + run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" From 7f1ff309ffcc5ee84d807d6b0178c444d9bf0357 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 18:39:44 -0800 Subject: [PATCH 34/60] crawling in fixes --- scripts/benchmark_helpers.py | 2 ++ scripts/upload_simple_app_benchmarks.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/benchmark_helpers.py b/scripts/benchmark_helpers.py index bdf1f0107c..1cb8548f58 100644 --- a/scripts/benchmark_helpers.py +++ b/scripts/benchmark_helpers.py @@ -1,5 +1,7 @@ """Helper functions for the benchmarking scripts.""" +from __future__ import annotations + import json diff --git a/scripts/upload_simple_app_benchmarks.py b/scripts/upload_simple_app_benchmarks.py index 080d027e3c..7afbdba5f5 100644 --- a/scripts/upload_simple_app_benchmarks.py +++ b/scripts/upload_simple_app_benchmarks.py @@ -38,7 +38,7 @@ def insert_benchmarking_data( with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: insert_query = """ INSERT INTO simple_app_benchmarks (os, python_version, commit_sha, time, pr_title, performance) - VALUES (%s, %s, %s, %s, %s); + VALUES (%s, %s, %s, %s, %s, %s); """ cursor.execute( insert_query, From 4945e4a4956d4f192e3b86ad7e0496e27fcafc48 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:01:53 -0800 Subject: [PATCH 35/60] only run on linux --- .github/workflows/benchmarks.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index f9fb13545f..ebac66f2b1 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -82,23 +82,8 @@ jobs: # 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] + os: [ubuntu-latest] python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] - exclude: - - os: windows-latest - python-version: '3.10.13' - - os: windows-latest - python-version: '3.9.18' - - os: windows-latest - python-version: '3.8.18' - include: - - os: windows-latest - python-version: '3.10.11' - - os: windows-latest - python-version: '3.9.13' - - os: windows-latest - python-version: '3.8.10' runs-on: ${{ matrix.os }} steps: From f55bb73ec4f8c080e8e6c4138a4a60580d4283a5 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:16:15 -0800 Subject: [PATCH 36/60] rename files --- .github/workflows/benchmarks.yml | 4 +- scripts/benchmark_helpers.py | 44 ------------------ ...d_upload.py => lighthouse_score_upload.py} | 45 ++----------------- ...arks.py => simple_app_benchmark_upload.py} | 40 ++++++++++++++++- 4 files changed, 44 insertions(+), 89 deletions(-) delete mode 100644 scripts/benchmark_helpers.py rename scripts/{reflex_web_benchmark_and_upload.py => lighthouse_score_upload.py} (71%) rename scripts/{upload_simple_app_benchmarks.py => simple_app_benchmark_upload.py} (70%) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index ebac66f2b1..87330be5e8 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -69,7 +69,7 @@ jobs: - name: Run Benchmarks # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/reflex_web_benchmark_and_upload.py "$GITHUB_SHA" ./integration/benchmarks/.lighthouseci + run: poetry run python scripts/lighthouse_score_upload.py "$GITHUB_SHA" ./integration/benchmarks/.lighthouseci env: GITHUB_SHA: ${{ github.sha }} PR_TITLE: ${{ github.event.pull_request.title }} @@ -102,4 +102,4 @@ jobs: - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/upload_simple_app_benchmarks.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" + run: poetry run python scripts/simple_app_benchmark_upload --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" diff --git a/scripts/benchmark_helpers.py b/scripts/benchmark_helpers.py deleted file mode 100644 index 1cb8548f58..0000000000 --- a/scripts/benchmark_helpers.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Helper functions for the benchmarking scripts.""" - -from __future__ import annotations - -import json - - -def extract_stats_from_json(json_file: str) -> list[dict]: - """Extracts the stats from the JSON data and returns them as a list of dictionaries. - - Args: - json_file: The JSON file to extract the stats data from. - - Returns: - list[dict]: The stats for each test. - """ - with open(json_file, "r") as file: - json_data = json.load(file) - - # Load the JSON data if it is a string, otherwise assume it's already a dictionary - data = json.loads(json_data) if isinstance(json_data, str) else json_data - - # Initialize an empty list to store the stats for each test - test_stats = [] - - # Iterate over each test in the 'benchmarks' list - for test in data.get("benchmarks", []): - stats = test.get("stats", {}) - test_name = test.get("name", "Unknown Test") - min_value = stats.get("min", None) - max_value = stats.get("max", None) - mean_value = stats.get("mean", None) - stdev_value = stats.get("stddev", None) - - test_stats.append( - { - "test_name": test_name, - "min": min_value, - "max": max_value, - "mean": mean_value, - "stdev": stdev_value, - } - ) - return test_stats diff --git a/scripts/reflex_web_benchmark_and_upload.py b/scripts/lighthouse_score_upload.py similarity index 71% rename from scripts/reflex_web_benchmark_and_upload.py rename to scripts/lighthouse_score_upload.py index b3cd12d0d1..35f32f5d9e 100644 --- a/scripts/reflex_web_benchmark_and_upload.py +++ b/scripts/lighthouse_score_upload.py @@ -8,14 +8,11 @@ from datetime import datetime import psycopg2 -import pytest -from benchmark_helpers import extract_stats_from_json def insert_benchmarking_data( db_connection_url: str, lighthouse_data: dict, - performance_data: list[dict], commit_sha: str, pr_title: str, ): @@ -24,13 +21,11 @@ def insert_benchmarking_data( Args: db_connection_url: The URL to connect to the database. lighthouse_data: The Lighthouse data to insert. - performance_data: The performance data to insert. commit_sha: The commit SHA to insert. pr_title: The PR title to insert. """ # Serialize the JSON data lighthouse_json = json.dumps(lighthouse_data) - performance_json = json.dumps(performance_data) # Get the current timestamp current_timestamp = datetime.now() @@ -38,14 +33,13 @@ def insert_benchmarking_data( # Connect to the database and insert the data with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: insert_query = """ - INSERT INTO benchmarks (lighthouse, performance, commit_sha, pr_title, time) - VALUES (%s, %s, %s, %s, %s); + INSERT INTO benchmarks (lighthouse, commit_sha, pr_title, time) + VALUES (%s, %s, %s, %s); """ cursor.execute( insert_query, ( lighthouse_json, - performance_json, commit_sha, pr_title, current_timestamp, @@ -91,33 +85,6 @@ def get_lighthouse_scores(directory_path: str) -> dict: return scores -def run_pytest_and_get_results(test_path=None) -> str: - """Runs pytest and returns the results. - - Args: - test_path: The path to the tests to run. - - Returns: - the name of the output file - """ - # Set the default path to the current directory if no path is provided - if not test_path: - test_path = os.getcwd() - - output_file = "benchmark_report.json" - - # Ensure you have installed the pytest-json plugin before running this - pytest_args = ["-v", f"--benchmark-json={output_file}", test_path] - - # Run pytest with the specified arguments - pytest.main(pytest_args) - - # Print ls of the current directory - print(os.listdir()) - - return output_file - - def main(): """Runs the benchmarks and inserts the results into the database.""" # Get the commit SHA and JSON directory from the command line arguments @@ -131,17 +98,11 @@ def main(): if db_url is None or pr_title is None: sys.exit("Missing environment variables") - # Run pytest and get the results - result_json_file = run_pytest_and_get_results() - cleaned_results = extract_stats_from_json(result_json_file) - # Get the Lighthouse scores lighthouse_scores = get_lighthouse_scores(json_dir) # Insert the data into the database - insert_benchmarking_data( - db_url, lighthouse_scores, cleaned_results, commit_sha, pr_title - ) + insert_benchmarking_data(db_url, lighthouse_scores, commit_sha, pr_title) if __name__ == "__main__": diff --git a/scripts/upload_simple_app_benchmarks.py b/scripts/simple_app_benchmark_upload.py similarity index 70% rename from scripts/upload_simple_app_benchmarks.py rename to scripts/simple_app_benchmark_upload.py index 7afbdba5f5..30e7bb98cf 100644 --- a/scripts/upload_simple_app_benchmarks.py +++ b/scripts/simple_app_benchmark_upload.py @@ -7,7 +7,45 @@ from datetime import datetime import psycopg2 -from benchmark_helpers import extract_stats_from_json + + +def extract_stats_from_json(json_file: str) -> list[dict]: + """Extracts the stats from the JSON data and returns them as a list of dictionaries. + + Args: + json_file: The JSON file to extract the stats data from. + + Returns: + list[dict]: The stats for each test. + """ + with open(json_file, "r") as file: + json_data = json.load(file) + + # Load the JSON data if it is a string, otherwise assume it's already a dictionary + data = json.loads(json_data) if isinstance(json_data, str) else json_data + + # Initialize an empty list to store the stats for each test + test_stats = [] + + # Iterate over each test in the 'benchmarks' list + for test in data.get("benchmarks", []): + stats = test.get("stats", {}) + test_name = test.get("name", "Unknown Test") + min_value = stats.get("min", None) + max_value = stats.get("max", None) + mean_value = stats.get("mean", None) + stdev_value = stats.get("stddev", None) + + test_stats.append( + { + "test_name": test_name, + "min": min_value, + "max": max_value, + "mean": mean_value, + "stdev": stdev_value, + } + ) + return test_stats def insert_benchmarking_data( From c7de50db5f0c646542faeca5b464711d999e2385 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:19:20 -0800 Subject: [PATCH 37/60] oops --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 87330be5e8..27f2ae148f 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -102,4 +102,4 @@ jobs: - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/simple_app_benchmark_upload --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" + run: poetry run python scripts/simple_app_benchmark_upload.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" From e8458c41d31376bc35866cba4b93ca1104d94b81 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Tue, 5 Mar 2024 20:28:27 -0800 Subject: [PATCH 38/60] remove fmt change on pyproject --- poetry.lock | 465 ++++++++++++++++++++++++------------------------- pyproject.toml | 47 +++-- 2 files changed, 248 insertions(+), 264 deletions(-) diff --git a/poetry.lock b/poetry.lock index 158a2597bf..c3e3a6200c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -132,18 +132,18 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "build" -version = "1.1.1" +version = "1.0.3" description = "A simple, correct Python build frontend" optional = false python-versions = ">= 3.7" files = [ - {file = "build-1.1.1-py3-none-any.whl", hash = "sha256:8ed0851ee76e6e38adce47e4bee3b51c771d86c64cf578d0c2245567ee200e73"}, - {file = "build-1.1.1.tar.gz", hash = "sha256:8eea65bb45b1aac2e734ba2cc8dad3a6d97d97901a395bd0ed3e7b46953d2a31"}, + {file = "build-1.0.3-py3-none-any.whl", hash = "sha256:589bf99a67df7c9cf07ec0ac0e5e2ea5d4b37ac63301c4986d1acb126aa83f8f"}, + {file = "build-1.0.3.tar.gz", hash = "sha256:538aab1b64f9828977f84bc63ae570b060a8ed1be419e7870b8b4fc5e6ea553b"}, ] [package.dependencies] colorama = {version = "*", markers = "os_name == \"nt\""} -importlib-metadata = {version = ">=4.6", markers = "python_full_version < \"3.10.2\""} +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} packaging = ">=19.0" pyproject_hooks = "*" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} @@ -167,75 +167,63 @@ files = [ [[package]] name = "cffi" -version = "1.15.1" +version = "1.16.0" description = "Foreign Function Interface for Python calling C code." optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, - {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, - {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, - {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, - {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, - {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, - {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, - {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, - {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, - {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, - {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, - {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, - {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, - {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, - {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, - {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, - {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, - {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, - {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, - {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, - {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, - {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, - {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, - {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, - {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, - {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, - {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, - {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, - {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, - {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, - {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, - {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, - {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, + {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, + {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, + {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, + {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, + {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, + {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, + {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, + {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, + {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, + {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, + {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, + {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, + {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, + {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, + {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, + {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, + {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, + {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, + {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, + {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, + {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, + {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, + {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, + {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, + {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, ] [package.dependencies] @@ -389,63 +377,63 @@ files = [ [[package]] name = "coverage" -version = "7.4.3" +version = "7.4.1" description = "Code coverage measurement for Python" optional = false python-versions = ">=3.8" files = [ - {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, - {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, - {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, - {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, - {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, - {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, - {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, - {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, - {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, - {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, - {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, - {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, - {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, - {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, - {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, - {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, - {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, - {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, - {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, - {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, - {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, - {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, - {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, - {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, - {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, - {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, - {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, - {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:077d366e724f24fc02dbfe9d946534357fda71af9764ff99d73c3c596001bbd7"}, + {file = "coverage-7.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0193657651f5399d433c92f8ae264aff31fc1d066deee4b831549526433f3f61"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d17bbc946f52ca67adf72a5ee783cd7cd3477f8f8796f59b4974a9b59cacc9ee"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3277f5fa7483c927fe3a7b017b39351610265308f5267ac6d4c2b64cc1d8d25"}, + {file = "coverage-7.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dceb61d40cbfcf45f51e59933c784a50846dc03211054bd76b421a713dcdf19"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6008adeca04a445ea6ef31b2cbaf1d01d02986047606f7da266629afee982630"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c61f66d93d712f6e03369b6a7769233bfda880b12f417eefdd4f16d1deb2fc4c"}, + {file = "coverage-7.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b9bb62fac84d5f2ff523304e59e5c439955fb3b7f44e3d7b2085184db74d733b"}, + {file = "coverage-7.4.1-cp310-cp310-win32.whl", hash = "sha256:f86f368e1c7ce897bf2457b9eb61169a44e2ef797099fb5728482b8d69f3f016"}, + {file = "coverage-7.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:869b5046d41abfea3e381dd143407b0d29b8282a904a19cb908fa24d090cc018"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b8ffb498a83d7e0305968289441914154fb0ef5d8b3157df02a90c6695978295"}, + {file = "coverage-7.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3cacfaefe6089d477264001f90f55b7881ba615953414999c46cc9713ff93c8c"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d6850e6e36e332d5511a48a251790ddc545e16e8beaf046c03985c69ccb2676"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18e961aa13b6d47f758cc5879383d27b5b3f3dcd9ce8cdbfdc2571fe86feb4dd"}, + {file = "coverage-7.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfd1e1b9f0898817babf840b77ce9fe655ecbe8b1b327983df485b30df8cc011"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6b00e21f86598b6330f0019b40fb397e705135040dbedc2ca9a93c7441178e74"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:536d609c6963c50055bab766d9951b6c394759190d03311f3e9fcf194ca909e1"}, + {file = "coverage-7.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7ac8f8eb153724f84885a1374999b7e45734bf93a87d8df1e7ce2146860edef6"}, + {file = "coverage-7.4.1-cp311-cp311-win32.whl", hash = "sha256:f3771b23bb3675a06f5d885c3630b1d01ea6cac9e84a01aaf5508706dba546c5"}, + {file = "coverage-7.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:9d2f9d4cc2a53b38cabc2d6d80f7f9b7e3da26b2f53d48f05876fef7956b6968"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f68ef3660677e6624c8cace943e4765545f8191313a07288a53d3da188bd8581"}, + {file = "coverage-7.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23b27b8a698e749b61809fb637eb98ebf0e505710ec46a8aa6f1be7dc0dc43a6"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e3424c554391dc9ef4a92ad28665756566a28fecf47308f91841f6c49288e66"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e0860a348bf7004c812c8368d1fc7f77fe8e4c095d661a579196a9533778e156"}, + {file = "coverage-7.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe558371c1bdf3b8fa03e097c523fb9645b8730399c14fe7721ee9c9e2a545d3"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3468cc8720402af37b6c6e7e2a9cdb9f6c16c728638a2ebc768ba1ef6f26c3a1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:02f2edb575d62172aa28fe00efe821ae31f25dc3d589055b3fb64d51e52e4ab1"}, + {file = "coverage-7.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ca6e61dc52f601d1d224526360cdeab0d0712ec104a2ce6cc5ccef6ed9a233bc"}, + {file = "coverage-7.4.1-cp312-cp312-win32.whl", hash = "sha256:ca7b26a5e456a843b9b6683eada193fc1f65c761b3a473941efe5a291f604c74"}, + {file = "coverage-7.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:85ccc5fa54c2ed64bd91ed3b4a627b9cce04646a659512a051fa82a92c04a448"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bdb0285a0202888d19ec6b6d23d5990410decb932b709f2b0dfe216d031d218"}, + {file = "coverage-7.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:918440dea04521f499721c039863ef95433314b1db00ff826a02580c1f503e45"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:379d4c7abad5afbe9d88cc31ea8ca262296480a86af945b08214eb1a556a3e4d"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b094116f0b6155e36a304ff912f89bbb5067157aff5f94060ff20bbabdc8da06"}, + {file = "coverage-7.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2f5968608b1fe2a1d00d01ad1017ee27efd99b3437e08b83ded9b7af3f6f766"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:10e88e7f41e6197ea0429ae18f21ff521d4f4490aa33048f6c6f94c6045a6a75"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a4a3907011d39dbc3e37bdc5df0a8c93853c369039b59efa33a7b6669de04c60"}, + {file = "coverage-7.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6d224f0c4c9c98290a6990259073f496fcec1b5cc613eecbd22786d398ded3ad"}, + {file = "coverage-7.4.1-cp38-cp38-win32.whl", hash = "sha256:23f5881362dcb0e1a92b84b3c2809bdc90db892332daab81ad8f642d8ed55042"}, + {file = "coverage-7.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:a07f61fc452c43cd5328b392e52555f7d1952400a1ad09086c4a8addccbd138d"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8e738a492b6221f8dcf281b67129510835461132b03024830ac0e554311a5c54"}, + {file = "coverage-7.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46342fed0fff72efcda77040b14728049200cbba1279e0bf1188f1f2078c1d70"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9641e21670c68c7e57d2053ddf6c443e4f0a6e18e547e86af3fad0795414a628"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aeb2c2688ed93b027eb0d26aa188ada34acb22dceea256d76390eea135083950"}, + {file = "coverage-7.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d12c923757de24e4e2110cf8832d83a886a4cf215c6e61ed506006872b43a6d1"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0491275c3b9971cdbd28a4595c2cb5838f08036bca31765bad5e17edf900b2c7"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8dfc5e195bbef80aabd81596ef52a1277ee7143fe419efc3c4d8ba2754671756"}, + {file = "coverage-7.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1a78b656a4d12b0490ca72651fe4d9f5e07e3c6461063a9b6265ee45eb2bdd35"}, + {file = "coverage-7.4.1-cp39-cp39-win32.whl", hash = "sha256:f90515974b39f4dea2f27c0959688621b46d96d5a626cf9c53dbc653a895c05c"}, + {file = "coverage-7.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:64e723ca82a84053dd7bfcc986bdb34af8d9da83c521c19d6b472bc6880e191a"}, + {file = "coverage-7.4.1-pp38.pp39.pp310-none-any.whl", hash = "sha256:32a8d985462e37cfdab611a6f95b09d7c091d07668fdc26e47a725ee575fe166"}, + {file = "coverage-7.4.1.tar.gz", hash = "sha256:1ed4b95480952b1a26d863e546fa5094564aa0065e1e5f0d4d0041f293251d04"}, ] [package.dependencies] @@ -534,19 +522,19 @@ test = ["anyio[trio] (>=3.2.1,<4.0.0)", "black (==23.1.0)", "coverage[toml] (>=6 [[package]] name = "filelock" -version = "3.12.4" +version = "3.13.1" description = "A platform independent file lock." optional = false python-versions = ">=3.8" files = [ - {file = "filelock-3.12.4-py3-none-any.whl", hash = "sha256:08c21d87ded6e2b9da6728c3dff51baf1dcecf973b768ef35bcbc3447edb9ad4"}, - {file = "filelock-3.12.4.tar.gz", hash = "sha256:2e6f249f1f3654291606e046b09f1fd5eac39b360664c27f5aad072012f8bcbd"}, + {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, + {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] [package.extras] -docs = ["furo (>=2023.7.26)", "sphinx (>=7.1.2)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3)", "diff-cover (>=7.7)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest-timeout (>=2.1)"] -typing = ["typing-extensions (>=4.7.1)"] +docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] +testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] +typing = ["typing-extensions (>=4.8)"] [[package]] name = "greenlet" @@ -652,13 +640,13 @@ files = [ [[package]] name = "httpcore" -version = "1.0.4" +version = "1.0.3" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpcore-1.0.4-py3-none-any.whl", hash = "sha256:ac418c1db41bade2ad53ae2f3834a3a0f5ae76b56cf5aa497d2d033384fc7d73"}, - {file = "httpcore-1.0.4.tar.gz", hash = "sha256:cb2839ccfcba0d2d3c1131d3c3e26dfc327326fbe7a5dc0dbfe9f6c9151bb022"}, + {file = "httpcore-1.0.3-py3-none-any.whl", hash = "sha256:9a6a501c3099307d9fd76ac244e08503427679b1e81ceb1d922485e2f2462ad2"}, + {file = "httpcore-1.0.3.tar.gz", hash = "sha256:5c0f9546ad17dac4d0772b0808856eb616eb8b48ce94f49ed819fd6982a8a544"}, ] [package.dependencies] @@ -669,7 +657,7 @@ h11 = ">=0.13,<0.15" asyncio = ["anyio (>=4.0,<5.0)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.25.0)"] +trio = ["trio (>=0.22.0,<0.24.0)"] [[package]] name = "httpx" @@ -741,13 +729,13 @@ testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs [[package]] name = "importlib-resources" -version = "6.1.2" +version = "6.1.1" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.1.2-py3-none-any.whl", hash = "sha256:9a0a862501dc38b68adebc82970140c9e4209fc99601782925178f8386339938"}, - {file = "importlib_resources-6.1.2.tar.gz", hash = "sha256:308abf8474e2dba5f867d279237cd4076482c3de7104a40b41426370e891549b"}, + {file = "importlib_resources-6.1.1-py3-none-any.whl", hash = "sha256:e8bf90d8213b486f428c9c39714b920041cb02c184686a3dee24905aaa8105d6"}, + {file = "importlib_resources-6.1.1.tar.gz", hash = "sha256:3893a00122eafde6894c59914446a512f728a0c1a45f9bb9b63721b6bacf0b4a"}, ] [package.dependencies] @@ -755,7 +743,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +testing = ["pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-ruff", "zipp (>=3.17)"] [[package]] name = "iniconfig" @@ -1086,40 +1074,40 @@ test = ["hypothesis (>=5.5.3)", "pytest (>=6.0)", "pytest-xdist (>=1.31)"] [[package]] name = "pandas" -version = "2.2.1" +version = "2.2.0" description = "Powerful data structures for data analysis, time series, and statistics" optional = false python-versions = ">=3.9" files = [ - {file = "pandas-2.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8df8612be9cd1c7797c93e1c5df861b2ddda0b48b08f2c3eaa0702cf88fb5f88"}, - {file = "pandas-2.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0f573ab277252ed9aaf38240f3b54cfc90fff8e5cab70411ee1d03f5d51f3944"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f02a3a6c83df4026e55b63c1f06476c9aa3ed6af3d89b4f04ea656ccdaaaa359"}, - {file = "pandas-2.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c38ce92cb22a4bea4e3929429aa1067a454dcc9c335799af93ba9be21b6beb51"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c2ce852e1cf2509a69e98358e8458775f89599566ac3775e70419b98615f4b06"}, - {file = "pandas-2.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53680dc9b2519cbf609c62db3ed7c0b499077c7fefda564e330286e619ff0dd9"}, - {file = "pandas-2.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:94e714a1cca63e4f5939cdce5f29ba8d415d85166be3441165edd427dc9f6bc0"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f821213d48f4ab353d20ebc24e4faf94ba40d76680642fb7ce2ea31a3ad94f9b"}, - {file = "pandas-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c70e00c2d894cb230e5c15e4b1e1e6b2b478e09cf27cc593a11ef955b9ecc81a"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97fbb5387c69209f134893abc788a6486dbf2f9e511070ca05eed4b930b1b02"}, - {file = "pandas-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101d0eb9c5361aa0146f500773395a03839a5e6ecde4d4b6ced88b7e5a1a6403"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7d2ed41c319c9fb4fd454fe25372028dfa417aacb9790f68171b2e3f06eae8cd"}, - {file = "pandas-2.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:af5d3c00557d657c8773ef9ee702c61dd13b9d7426794c9dfeb1dc4a0bf0ebc7"}, - {file = "pandas-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:06cf591dbaefb6da9de8472535b185cba556d0ce2e6ed28e21d919704fef1a9e"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:88ecb5c01bb9ca927ebc4098136038519aa5d66b44671861ffab754cae75102c"}, - {file = "pandas-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:04f6ec3baec203c13e3f8b139fb0f9f86cd8c0b94603ae3ae8ce9a422e9f5bee"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a935a90a76c44fe170d01e90a3594beef9e9a6220021acfb26053d01426f7dc2"}, - {file = "pandas-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c391f594aae2fd9f679d419e9a4d5ba4bce5bb13f6a989195656e7dc4b95c8f0"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9d1265545f579edf3f8f0cb6f89f234f5e44ba725a34d86535b1a1d38decbccc"}, - {file = "pandas-2.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:11940e9e3056576ac3244baef2fedade891977bcc1cb7e5cc8f8cc7d603edc89"}, - {file = "pandas-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:4acf681325ee1c7f950d058b05a820441075b0dd9a2adf5c4835b9bc056bf4fb"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9bd8a40f47080825af4317d0340c656744f2bfdb6819f818e6ba3cd24c0e1397"}, - {file = "pandas-2.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:df0c37ebd19e11d089ceba66eba59a168242fc6b7155cba4ffffa6eccdfb8f16"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:739cc70eaf17d57608639e74d63387b0d8594ce02f69e7a0b046f117974b3019"}, - {file = "pandas-2.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9d3558d263073ed95e46f4650becff0c5e1ffe0fc3a015de3c79283dfbdb3df"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4aa1d8707812a658debf03824016bf5ea0d516afdea29b7dc14cf687bc4d4ec6"}, - {file = "pandas-2.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:76f27a809cda87e07f192f001d11adc2b930e93a2b0c4a236fde5429527423be"}, - {file = "pandas-2.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1ba21b1d5c0e43416218db63037dbe1a01fc101dc6e6024bcad08123e48004ab"}, - {file = "pandas-2.2.1.tar.gz", hash = "sha256:0ab90f87093c13f3e8fa45b48ba9f39181046e8f3317d3aadb2fffbb1b978572"}, + {file = "pandas-2.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8108ee1712bb4fa2c16981fba7e68b3f6ea330277f5ca34fa8d557e986a11670"}, + {file = "pandas-2.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:736da9ad4033aeab51d067fc3bd69a0ba36f5a60f66a527b3d72e2030e63280a"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38e0b4fc3ddceb56ec8a287313bc22abe17ab0eb184069f08fc6a9352a769b18"}, + {file = "pandas-2.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20404d2adefe92aed3b38da41d0847a143a09be982a31b85bc7dd565bdba0f4e"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7ea3ee3f125032bfcade3a4cf85131ed064b4f8dd23e5ce6fa16473e48ebcaf5"}, + {file = "pandas-2.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f9670b3ac00a387620489dfc1bca66db47a787f4e55911f1293063a78b108df1"}, + {file = "pandas-2.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:5a946f210383c7e6d16312d30b238fd508d80d927014f3b33fb5b15c2f895430"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a1b438fa26b208005c997e78672f1aa8138f67002e833312e6230f3e57fa87d5"}, + {file = "pandas-2.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8ce2fbc8d9bf303ce54a476116165220a1fedf15985b09656b4b4275300e920b"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2707514a7bec41a4ab81f2ccce8b382961a29fbe9492eab1305bb075b2b1ff4f"}, + {file = "pandas-2.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85793cbdc2d5bc32620dc8ffa715423f0c680dacacf55056ba13454a5be5de88"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:cfd6c2491dc821b10c716ad6776e7ab311f7df5d16038d0b7458bc0b67dc10f3"}, + {file = "pandas-2.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a146b9dcacc3123aa2b399df1a284de5f46287a4ab4fbfc237eac98a92ebcb71"}, + {file = "pandas-2.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:fbc1b53c0e1fdf16388c33c3cca160f798d38aea2978004dd3f4d3dec56454c9"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a41d06f308a024981dcaa6c41f2f2be46a6b186b902c94c2674e8cb5c42985bc"}, + {file = "pandas-2.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:159205c99d7a5ce89ecfc37cb08ed179de7783737cea403b295b5eda8e9c56d1"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1e1f3861ea9132b32f2133788f3b14911b68102d562715d71bd0013bc45440"}, + {file = "pandas-2.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:761cb99b42a69005dec2b08854fb1d4888fdf7b05db23a8c5a099e4b886a2106"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a20628faaf444da122b2a64b1e5360cde100ee6283ae8effa0d8745153809a2e"}, + {file = "pandas-2.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f5be5d03ea2073627e7111f61b9f1f0d9625dc3c4d8dda72cc827b0c58a1d042"}, + {file = "pandas-2.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:a626795722d893ed6aacb64d2401d017ddc8a2341b49e0384ab9bf7112bdec30"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9f66419d4a41132eb7e9a73dcec9486cf5019f52d90dd35547af11bc58f8637d"}, + {file = "pandas-2.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57abcaeda83fb80d447f28ab0cc7b32b13978f6f733875ebd1ed14f8fbc0f4ab"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e60f1f7dba3c2d5ca159e18c46a34e7ca7247a73b5dd1a22b6d59707ed6b899a"}, + {file = "pandas-2.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb61dc8567b798b969bcc1fc964788f5a68214d333cade8319c7ab33e2b5d88a"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:52826b5f4ed658fa2b729264d63f6732b8b29949c7fd234510d57c61dbeadfcd"}, + {file = "pandas-2.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bde2bc699dbd80d7bc7f9cab1e23a95c4375de615860ca089f34e7c64f4a8de7"}, + {file = "pandas-2.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:3de918a754bbf2da2381e8a3dcc45eede8cd7775b047b923f9006d5f876802ae"}, + {file = "pandas-2.2.0.tar.gz", hash = "sha256:30b83f7c3eb217fb4d1b494a57a2fda5444f17834f5df2de6b2ffff68dc3c8e2"}, ] [package.dependencies] @@ -1151,7 +1139,6 @@ parquet = ["pyarrow (>=10.0.1)"] performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] plot = ["matplotlib (>=3.6.3)"] postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] spss = ["pyreadstat (>=1.2.0)"] sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] @@ -1255,13 +1242,13 @@ xmp = ["defusedxml"] [[package]] name = "pipdeptree" -version = "2.16.1" +version = "2.15.1" description = "Command line utility to show dependency tree of packages." optional = false python-versions = ">=3.8" files = [ - {file = "pipdeptree-2.16.1-py3-none-any.whl", hash = "sha256:7131023df30220678b6a42c7372ce07453b39e0b3df935b129e832f0acf96b8a"}, - {file = "pipdeptree-2.16.1.tar.gz", hash = "sha256:f1ca64ce4aff9373613711048b9c4e8106ad955dea0dd962b7974fa168d7650a"}, + {file = "pipdeptree-2.15.1-py3-none-any.whl", hash = "sha256:0abe6b5b9f86a79ea023a633d7526de14b64b848bbdf980c610d08d679c8329d"}, + {file = "pipdeptree-2.15.1.tar.gz", hash = "sha256:e4ae44fc9d0c747125316981804d7786e9d97bf59c128249b8ea272a7cbdecc4"}, ] [package.extras] @@ -1604,13 +1591,13 @@ dev = ["pre-commit", "pytest-asyncio", "tox"] [[package]] name = "python-dateutil" -version = "2.9.0.post0" +version = "2.8.2" description = "Extensions to the standard Python datetime module" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, ] [package.dependencies] @@ -1804,13 +1791,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.7.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.7.0-py3-none-any.whl", hash = "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"}, + {file = "rich-13.7.0.tar.gz", hash = "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa"}, ] [package.dependencies] @@ -1848,13 +1835,13 @@ files = [ [[package]] name = "selenium" -version = "4.18.1" +version = "4.18.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "selenium-4.18.1-py3-none-any.whl", hash = "sha256:b24a3cdd2d47c29832e81345bfcde0c12bb608738013e53c781b211b418df241"}, - {file = "selenium-4.18.1.tar.gz", hash = "sha256:a11f67afa8bfac6b77e148c987b33f6b14eb1cae4d352722a75de1f26e3f0ae2"}, + {file = "selenium-4.18.0-py3-none-any.whl", hash = "sha256:d0ac1ab0aa50dffad8050e7f0170395ea23bcecccf7d59b69bc430e47eeb9dac"}, + {file = "selenium-4.18.0.tar.gz", hash = "sha256:8912e690d4e72350ca733ad28be894e87683064de3304e5f5b5b386697a6c47d"}, ] [package.dependencies] @@ -1910,13 +1897,13 @@ files = [ [[package]] name = "sniffio" -version = "1.3.1" +version = "1.3.0" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] [[package]] @@ -1932,60 +1919,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.28" +version = "2.0.27" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e0b148ab0438f72ad21cb004ce3bdaafd28465c4276af66df3b9ecd2037bf252"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bbda76961eb8f27e6ad3c84d1dc56d5bc61ba8f02bd20fcf3450bd421c2fcc9c"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:feea693c452d85ea0015ebe3bb9cd15b6f49acc1a31c28b3c50f4db0f8fb1e71"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5da98815f82dce0cb31fd1e873a0cb30934971d15b74e0d78cf21f9e1b05953f"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4a5adf383c73f2d49ad15ff363a8748319ff84c371eed59ffd0127355d6ea1da"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56856b871146bfead25fbcaed098269d90b744eea5cb32a952df00d542cdd368"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win32.whl", hash = "sha256:943aa74a11f5806ab68278284a4ddd282d3fb348a0e96db9b42cb81bf731acdc"}, - {file = "SQLAlchemy-2.0.28-cp310-cp310-win_amd64.whl", hash = "sha256:c6c4da4843e0dabde41b8f2e8147438330924114f541949e6318358a56d1875a"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46a3d4e7a472bfff2d28db838669fc437964e8af8df8ee1e4548e92710929adc"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0d3dd67b5d69794cfe82862c002512683b3db038b99002171f624712fa71aeaa"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c61e2e41656a673b777e2f0cbbe545323dbe0d32312f590b1bc09da1de6c2a02"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0315d9125a38026227f559488fe7f7cee1bd2fbc19f9fd637739dc50bb6380b2"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af8ce2d31679006e7b747d30a89cd3ac1ec304c3d4c20973f0f4ad58e2d1c4c9"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:81ba314a08c7ab701e621b7ad079c0c933c58cdef88593c59b90b996e8b58fa5"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win32.whl", hash = "sha256:1ee8bd6d68578e517943f5ebff3afbd93fc65f7ef8f23becab9fa8fb315afb1d"}, - {file = "SQLAlchemy-2.0.28-cp311-cp311-win_amd64.whl", hash = "sha256:ad7acbe95bac70e4e687a4dc9ae3f7a2f467aa6597049eeb6d4a662ecd990bb6"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d3499008ddec83127ab286c6f6ec82a34f39c9817f020f75eca96155f9765097"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9b66fcd38659cab5d29e8de5409cdf91e9986817703e1078b2fdaad731ea66f5"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea30da1e76cb1acc5b72e204a920a3a7678d9d52f688f087dc08e54e2754c67"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:124202b4e0edea7f08a4db8c81cc7859012f90a0d14ba2bf07c099aff6e96462"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e23b88c69497a6322b5796c0781400692eca1ae5532821b39ce81a48c395aae9"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b6303bfd78fb3221847723104d152e5972c22367ff66edf09120fcde5ddc2e2"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win32.whl", hash = "sha256:a921002be69ac3ab2cf0c3017c4e6a3377f800f1fca7f254c13b5f1a2f10022c"}, - {file = "SQLAlchemy-2.0.28-cp312-cp312-win_amd64.whl", hash = "sha256:b4a2cf92995635b64876dc141af0ef089c6eea7e05898d8d8865e71a326c0385"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e91b5e341f8c7f1e5020db8e5602f3ed045a29f8e27f7f565e0bdee3338f2c7"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45c7b78dfc7278329f27be02c44abc0d69fe235495bb8e16ec7ef1b1a17952db"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3eba73ef2c30695cb7eabcdb33bb3d0b878595737479e152468f3ba97a9c22a4"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5df5d1dafb8eee89384fb7a1f79128118bc0ba50ce0db27a40750f6f91aa99d5"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2858bbab1681ee5406650202950dc8f00e83b06a198741b7c656e63818633526"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win32.whl", hash = "sha256:9461802f2e965de5cff80c5a13bc945abea7edaa1d29360b485c3d2b56cdb075"}, - {file = "SQLAlchemy-2.0.28-cp37-cp37m-win_amd64.whl", hash = "sha256:a6bec1c010a6d65b3ed88c863d56b9ea5eeefdf62b5e39cafd08c65f5ce5198b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:843a882cadebecc655a68bd9a5b8aa39b3c52f4a9a5572a3036fb1bb2ccdc197"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:dbb990612c36163c6072723523d2be7c3eb1517bbdd63fe50449f56afafd1133"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd7e4baf9161d076b9a7e432fce06217b9bd90cfb8f1d543d6e8c4595627edb9"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e0a5354cb4de9b64bccb6ea33162cb83e03dbefa0d892db88a672f5aad638a75"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fffcc8edc508801ed2e6a4e7b0d150a62196fd28b4e16ab9f65192e8186102b6"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:aca7b6d99a4541b2ebab4494f6c8c2f947e0df4ac859ced575238e1d6ca5716b"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win32.whl", hash = "sha256:8c7f10720fc34d14abad5b647bc8202202f4948498927d9f1b4df0fb1cf391b7"}, - {file = "SQLAlchemy-2.0.28-cp38-cp38-win_amd64.whl", hash = "sha256:243feb6882b06a2af68ecf4bec8813d99452a1b62ba2be917ce6283852cf701b"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:fc4974d3684f28b61b9a90fcb4c41fb340fd4b6a50c04365704a4da5a9603b05"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:87724e7ed2a936fdda2c05dbd99d395c91ea3c96f029a033a4a20e008dd876bf"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68722e6a550f5de2e3cfe9da6afb9a7dd15ef7032afa5651b0f0c6b3adb8815d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328529f7c7f90adcd65aed06a161851f83f475c2f664a898af574893f55d9e53"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:df40c16a7e8be7413b885c9bf900d402918cc848be08a59b022478804ea076b8"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:426f2fa71331a64f5132369ede5171c52fd1df1bd9727ce621f38b5b24f48750"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win32.whl", hash = "sha256:33157920b233bc542ce497a81a2e1452e685a11834c5763933b440fedd1d8e2d"}, - {file = "SQLAlchemy-2.0.28-cp39-cp39-win_amd64.whl", hash = "sha256:2f60843068e432311c886c5f03c4664acaef507cf716f6c60d5fde7265be9d7b"}, - {file = "SQLAlchemy-2.0.28-py3-none-any.whl", hash = "sha256:78bb7e8da0183a8301352d569900d9d3594c48ac21dc1c2ec6b3121ed8b6c986"}, - {file = "SQLAlchemy-2.0.28.tar.gz", hash = "sha256:dd53b6c4e6d960600fd6532b79ee28e2da489322fcf6648738134587faf767b6"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d04e579e911562f1055d26dab1868d3e0bb905db3bccf664ee8ad109f035618a"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fa67d821c1fd268a5a87922ef4940442513b4e6c377553506b9db3b83beebbd8"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c7a596d0be71b7baa037f4ac10d5e057d276f65a9a611c46970f012752ebf2d"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:954d9735ee9c3fa74874c830d089a815b7b48df6f6b6e357a74130e478dbd951"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5cd20f58c29bbf2680039ff9f569fa6d21453fbd2fa84dbdb4092f006424c2e6"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:03f448ffb731b48323bda68bcc93152f751436ad6037f18a42b7e16af9e91c07"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win32.whl", hash = "sha256:d997c5938a08b5e172c30583ba6b8aad657ed9901fc24caf3a7152eeccb2f1b4"}, + {file = "SQLAlchemy-2.0.27-cp310-cp310-win_amd64.whl", hash = "sha256:eb15ef40b833f5b2f19eeae65d65e191f039e71790dd565c2af2a3783f72262f"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6c5bad7c60a392850d2f0fee8f355953abaec878c483dd7c3836e0089f046bf6"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3012ab65ea42de1be81fff5fb28d6db893ef978950afc8130ba707179b4284a"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbcd77c4d94b23e0753c5ed8deba8c69f331d4fd83f68bfc9db58bc8983f49cd"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d177b7e82f6dd5e1aebd24d9c3297c70ce09cd1d5d37b43e53f39514379c029c"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:680b9a36029b30cf063698755d277885d4a0eab70a2c7c6e71aab601323cba45"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1306102f6d9e625cebaca3d4c9c8f10588735ef877f0360b5cdb4fdfd3fd7131"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win32.whl", hash = "sha256:5b78aa9f4f68212248aaf8943d84c0ff0f74efc65a661c2fc68b82d498311fd5"}, + {file = "SQLAlchemy-2.0.27-cp311-cp311-win_amd64.whl", hash = "sha256:15e19a84b84528f52a68143439d0c7a3a69befcd4f50b8ef9b7b69d2628ae7c4"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0de1263aac858f288a80b2071990f02082c51d88335a1db0d589237a3435fe71"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce850db091bf7d2a1f2fdb615220b968aeff3849007b1204bf6e3e50a57b3d32"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dfc936870507da96aebb43e664ae3a71a7b96278382bcfe84d277b88e379b18"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4fbe6a766301f2e8a4519f4500fe74ef0a8509a59e07a4085458f26228cd7cc"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4535c49d961fe9a77392e3a630a626af5baa967172d42732b7a43496c8b28876"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0fb3bffc0ced37e5aa4ac2416f56d6d858f46d4da70c09bb731a246e70bff4d5"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win32.whl", hash = "sha256:7f470327d06400a0aa7926b375b8e8c3c31d335e0884f509fe272b3c700a7254"}, + {file = "SQLAlchemy-2.0.27-cp312-cp312-win_amd64.whl", hash = "sha256:f9374e270e2553653d710ece397df67db9d19c60d2647bcd35bfc616f1622dcd"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e97cf143d74a7a5a0f143aa34039b4fecf11343eed66538610debc438685db4a"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7b5a3e2120982b8b6bd1d5d99e3025339f7fb8b8267551c679afb39e9c7c7f1"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e36aa62b765cf9f43a003233a8c2d7ffdeb55bc62eaa0a0380475b228663a38f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5ada0438f5b74c3952d916c199367c29ee4d6858edff18eab783b3978d0db16d"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b1d9d1bfd96eef3c3faedb73f486c89e44e64e40e5bfec304ee163de01cf996f"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win32.whl", hash = "sha256:ca891af9f3289d24a490a5fde664ea04fe2f4984cd97e26de7442a4251bd4b7c"}, + {file = "SQLAlchemy-2.0.27-cp37-cp37m-win_amd64.whl", hash = "sha256:fd8aafda7cdff03b905d4426b714601c0978725a19efc39f5f207b86d188ba01"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec1f5a328464daf7a1e4e385e4f5652dd9b1d12405075ccba1df842f7774b4fc"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad862295ad3f644e3c2c0d8b10a988e1600d3123ecb48702d2c0f26771f1c396"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48217be1de7d29a5600b5c513f3f7664b21d32e596d69582be0a94e36b8309cb"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e56afce6431450442f3ab5973156289bd5ec33dd618941283847c9fd5ff06bf"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:611068511b5531304137bcd7fe8117c985d1b828eb86043bd944cebb7fae3910"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b86abba762ecfeea359112b2bb4490802b340850bbee1948f785141a5e020de8"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win32.whl", hash = "sha256:30d81cc1192dc693d49d5671cd40cdec596b885b0ce3b72f323888ab1c3863d5"}, + {file = "SQLAlchemy-2.0.27-cp38-cp38-win_amd64.whl", hash = "sha256:120af1e49d614d2525ac247f6123841589b029c318b9afbfc9e2b70e22e1827d"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d07ee7793f2aeb9b80ec8ceb96bc8cc08a2aec8a1b152da1955d64e4825fcbac"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cb0845e934647232b6ff5150df37ceffd0b67b754b9fdbb095233deebcddbd4a"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc19ae2e07a067663dd24fca55f8ed06a288384f0e6e3910420bf4b1270cc51"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b90053be91973a6fb6020a6e44382c97739736a5a9d74e08cc29b196639eb979"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2f5c9dfb0b9ab5e3a8a00249534bdd838d943ec4cfb9abe176a6c33408430230"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33e8bde8fff203de50399b9039c4e14e42d4d227759155c21f8da4a47fc8053c"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win32.whl", hash = "sha256:d873c21b356bfaf1589b89090a4011e6532582b3a8ea568a00e0c3aab09399dd"}, + {file = "SQLAlchemy-2.0.27-cp39-cp39-win_amd64.whl", hash = "sha256:ff2f1b7c963961d41403b650842dc2039175b906ab2093635d8319bef0b7d620"}, + {file = "SQLAlchemy-2.0.27-py3-none-any.whl", hash = "sha256:1ab4e0448018d01b142c916cc7119ca573803a4745cfe341b8f95657812700ac"}, + {file = "SQLAlchemy-2.0.27.tar.gz", hash = "sha256:86a6ed69a71fe6b88bf9331594fa390a2adda4a49b5c06f98e47bf0d392534f8"}, ] [package.dependencies] @@ -2192,13 +2179,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.9.0-py3-none-any.whl", hash = "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"}, + {file = "typing_extensions-4.9.0.tar.gz", hash = "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783"}, ] [[package]] @@ -2270,13 +2257,13 @@ standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", [[package]] name = "virtualenv" -version = "20.25.1" +version = "20.25.0" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, - {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, + {file = "virtualenv-20.25.0-py3-none-any.whl", hash = "sha256:4238949c5ffe6876362d9c0180fc6c3a824a7b12b80604eeb8085f2ed7460de3"}, + {file = "virtualenv-20.25.0.tar.gz", hash = "sha256:bf51c0d9c7dd63ea8e44086fa1e4fb1093a31e963b86959257378aef020e1f1b"}, ] [package.dependencies] diff --git a/pyproject.toml b/pyproject.toml index 48ac255710..7c3f98ec2f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,9 +14,16 @@ readme = "README.md" homepage = "https://reflex.dev" repository = "https://github.com/reflex-dev/reflex" documentation = "https://reflex.dev/docs/getting-started/introduction" -keywords = ["web", "framework"] -classifiers = ["Development Status :: 4 - Beta"] -packages = [{ include = "reflex" }] +keywords = [ + "web", + "framework", +] +classifiers = [ + "Development Status :: 4 - Beta", +] +packages = [ + {include = "reflex"} +] [tool.poetry.dependencies] python = "^3.8" @@ -33,19 +40,19 @@ rich = "^13.0.0" sqlmodel = "^0.0.14" typer = ">=0.4.2,<1" uvicorn = [ - { version = "^0.24.0", python = ">=3.12" }, - { version = "^0.20.0", python = "<3.12" }, + {version = "^0.24.0", python = ">=3.12"}, + {version = "^0.20.0", python = "<3.12"}, ] watchdog = "^2.3.1" watchfiles = "^0.19.0" starlette-admin = "^0.9.0" alembic = "^1.11.1" platformdirs = "^3.10.0" -distro = { version = "^1.8.0", platform = "linux" } +distro = {version = "^1.8.0", platform = "linux"} python-engineio = "!=4.6.0" wrapt = [ - { version = "^1.14.0", python = ">=3.11" }, - { version = "^1.11.0", python = "<3.11" }, + {version = "^1.14.0", python = ">=3.11"}, + {version = "^1.11.0", python = "<3.11"}, ] packaging = "^23.1" reflex-hosting-cli = ">=0.1.2" @@ -66,13 +73,15 @@ pytest-cov = "^4.0.0" black = "^22.10.0" ruff = "^0.0.244" pandas = [ - { version = "^2.1.1", python = ">=3.9,<3.13" }, - { version = "^1.5.3", python = ">=3.8,<3.9" }, + {version = "^2.1.1", python = ">=3.9,<3.13"}, + {version = "^1.5.3", python = ">=3.8,<3.9"}, +] +pillow = [ + {version = "^10.0.0", python = ">=3.8,<4.0"} ] -pillow = [{ version = "^10.0.0", python = ">=3.8,<4.0" }] plotly = "^5.13.0" asynctest = "^0.13.0" -pre-commit = { version = "^3.2.1", python = ">=3.8,<4.0" } +pre-commit = {version = "^3.2.1", python = ">=3.8,<4.0"} selenium = "^4.11.0" types-tabulate = "^0.9.0.3" pytest-benchmark = "^4.0.0" @@ -90,19 +99,7 @@ build-backend = "poetry.core.masonry.api" select = ["B", "D", "E", "F", "I", "SIM", "W"] -ignore = [ - "B008", - "D203", - "D205", - "D213", - "D401", - "D406", - "D407", - "E501", - "F403", - "F405", - "F541", -] +ignore = ["B008", "D203", "D205", "D213", "D401", "D406", "D407", "E501", "F403", "F405", "F541"] target-version = "py37" From f783f3824e8ca84bfb7bef3eddec7517c3638314 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:18:56 -0800 Subject: [PATCH 39/60] Revert "only run on linux" This reverts commit 4945e4a4956d4f192e3b86ad7e0496e27fcafc48. --- .github/workflows/benchmarks.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 27f2ae148f..b89f113000 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -82,8 +82,23 @@ jobs: # Prioritize getting more information out of the workflow (even if something fails) fail-fast: false matrix: - os: [ubuntu-latest] + # Show OS combos first in GUI + os: [ubuntu-latest, windows-latest, macos-latest] python-version: ['3.8.18', '3.9.18', '3.10.13', '3.11.5', '3.12.0'] + exclude: + - os: windows-latest + python-version: '3.10.13' + - os: windows-latest + python-version: '3.9.18' + - os: windows-latest + python-version: '3.8.18' + include: + - os: windows-latest + python-version: '3.10.11' + - os: windows-latest + python-version: '3.9.13' + - os: windows-latest + python-version: '3.8.10' runs-on: ${{ matrix.os }} steps: From 98e994dc137e9994505174f070822b7d54eb6e0d Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:20:52 -0800 Subject: [PATCH 40/60] try headless --- .github/workflows/benchmarks.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index b89f113000..4bdbb0c8cb 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -111,8 +111,10 @@ jobs: - name: Install additional dependencies for appharness test and DB run: poetry run pip install pyvirtualdisplay pillow psycopg2-binary - name: Run benchmark tests + env: + APP_HARNESS_HEADLESS: 1 + PYTHONUNBUFFERED: 1 run: | - export PYTHONUNBUFFERED=1 poetry run pytest -v integration/benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} - name: Upload benchmark results # Only run if the database creds are available in this context. From dc9ccb320895dc1f1e1b986deeafa2f21a7a9583 Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:26:41 -0800 Subject: [PATCH 41/60] headless in conftest --- integration/conftest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration/conftest.py b/integration/conftest.py index 49c33e48ff..c3d57a0ae1 100644 --- a/integration/conftest.py +++ b/integration/conftest.py @@ -1,4 +1,5 @@ """Shared conftest for all integration tests.""" + import os import re from pathlib import Path @@ -20,7 +21,7 @@ def xvfb(): Yields: the pyvirtualdisplay object that the browser will be open on """ - if os.environ.get("GITHUB_ACTIONS"): + if os.environ.get("GITHUB_ACTIONS") and not os.environ.get("APP_HARNESS_HEADLESS"): from pyvirtualdisplay.smartdisplay import ( # pyright: ignore [reportMissingImports] SmartDisplay, ) From ea1bec67f55a4205ed90a0f5e48ed7f90896e7ed Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 8 Mar 2024 14:58:12 +0000 Subject: [PATCH 42/60] refactor benchmarks to use number metrics --- integration/benchmarks/test_base_apps.py | 268 ------- .../test_benchmark_compile_varying_pages.py | 752 ++++++++++++++++++ integration/benchmarks/test_medium_apps.py | 173 ---- 3 files changed, 752 insertions(+), 441 deletions(-) delete mode 100644 integration/benchmarks/test_base_apps.py create mode 100644 integration/benchmarks/test_benchmark_compile_varying_pages.py delete mode 100644 integration/benchmarks/test_medium_apps.py diff --git a/integration/benchmarks/test_base_apps.py b/integration/benchmarks/test_base_apps.py deleted file mode 100644 index 4ac522ecb5..0000000000 --- a/integration/benchmarks/test_base_apps.py +++ /dev/null @@ -1,268 +0,0 @@ -"""Benchmark tests for base apps.""" - -import os -import time -from typing import Generator - -import pytest - -from reflex import constants -from reflex.testing import AppHarness, chdir -from reflex.utils import build, path_ops - - -def BaseApp(): - """Test that background tasks work as expected.""" - from rxconfig import config # type: ignore - - 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.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", - ) - - app = rx.App(state=rx.State) - app.add_page(index) - - -def BaseApp2(): - """Test that background tasks work as expected.""" - from rxconfig import config # type: ignore - from typing import Tuple - - 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) - - def index() -> rx.Component: - return 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", - ) - - def selection() -> rx.Component: - return rx.vstack( - rx.hstack( - rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - 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, # type: ignore - ), - 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, # type: ignore - ), - align_items="left", - width="100%", - ), - ), - spacing="4", - ), - width="100%", - ) - - app = rx.App(state=rx.State) - app.add_page(index) - app.add_page(selection) - - -@pytest.fixture(scope="session") -def base_app( - tmp_path_factory, -) -> Generator[AppHarness, None, None]: - """Start Base app at tmp_path via AppHarness. - - Args: - tmp_path_factory: pytest tmp_path_factory fixture - - Yields: - running AppHarness instance - """ - root = tmp_path_factory.mktemp(f"base_app") - - yield AppHarness.create(root=root, app_source=BaseApp) # type: ignore - - -@pytest.fixture(scope="session") -def base_app_two_pages( - tmp_path_factory, -) -> Generator[AppHarness, None, None]: - """Start Base app at tmp_path via AppHarness. - - Args: - tmp_path_factory: pytest tmp_path_factory fixture - - Yields: - running AppHarness instance - """ - root = tmp_path_factory.mktemp(f"base_app_two_pages") - - yield AppHarness.create(root=root, app_source=BaseApp2) # type: ignore - - -@pytest.mark.benchmark( - group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False -) -def test_base_app_compile_time_cold(benchmark, base_app): - def setup(): - with chdir(base_app.app_path): - base_app._initialize_app() - build.setup_frontend(base_app.app_path) - - def benchmark_fn(): - with chdir(base_app.app_path): - base_app.app_instance.compile_() - - benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) - - -@pytest.mark.benchmark( - group="blank template", timer=time.perf_counter, disable_gc=True, warmup=False -) -def test_base_app_two_pages_compile_time_cold(benchmark, base_app_two_pages): - def setup(): - with chdir(base_app_two_pages.app_path): - base_app_two_pages._initialize_app() - build.setup_frontend(base_app_two_pages.app_path) - - def benchmark_fn(): - with chdir(base_app_two_pages.app_path): - base_app_two_pages.app_instance.compile_() - path_ops.rm( - os.path.join( - constants.Dirs.WEB, "reflex.install_frontend_packages.cached" - ) - ) - path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) - - benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) - - -@pytest.mark.benchmark( - group="blank template", - min_rounds=10, - timer=time.perf_counter, - disable_gc=True, - warmup=False, -) -def test_base_app_compile_time_warm(benchmark, base_app): - def benchmark_fn(): - with chdir(base_app.app_path): - base_app.app_instance.compile_() - - benchmark.pedantic(benchmark_fn, rounds=10) - - -@pytest.mark.benchmark( - group="blank template", - min_rounds=10, - timer=time.perf_counter, - disable_gc=True, - warmup=False, -) -def test_base_app_two_pages_compile_time_warm(benchmark, base_app_two_pages): - def benchmark_fn(): - with chdir(base_app_two_pages.app_path): - base_app_two_pages.app_instance.compile_() - path_ops.rm( - os.path.join( - constants.Dirs.WEB, "reflex.install_frontend_packages.cached" - ) - ) - path_ops.rm(os.path.join(constants.Dirs.WEB, "node_modules")) - - benchmark.pedantic(benchmark_fn) - - -# TODO: -def test_base_app_hot_reload(): - pass - - -# TODO: -def test_base_app_two_pages_hot_reload(): - pass diff --git a/integration/benchmarks/test_benchmark_compile_varying_pages.py b/integration/benchmarks/test_benchmark_compile_varying_pages.py new file mode 100644 index 0000000000..9cf0a0301a --- /dev/null +++ b/integration/benchmarks/test_benchmark_compile_varying_pages.py @@ -0,0 +1,752 @@ +"""Benchmark tests for medium sized apps.""" + +from __future__ import annotations + +import time +from typing import Generator + +import pytest + +from reflex.testing import AppHarness, chdir +from reflex.utils import build +from reflex.utils import path_ops +from reflex import constants +from reflex.compiler import utils + + +def AppWithOnePage(): + """Test that background tasks work as expected.""" + + from rxconfig import config # type: ignore + + 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.chakra.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(): + """Test that background tasks work as expected.""" + + def register_pages(num): + from rxconfig import config # type: ignore + from typing import Tuple + + 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, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + 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, # type: ignore + ), + 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, # type: ignore + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + 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}") + return app + + app = register_pages(10) + + +def AppWithHundredPages(): + def register_pages(num): + from rxconfig import config # type: ignore + from typing import Tuple + + 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, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + 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, # type: ignore + ), + 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, # type: ignore + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + 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}") + return app + + app = register_pages(100) + + +def AppWithThousandPages(): + def register_pages(num): + from rxconfig import config # type: ignore + from typing import Tuple + + 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, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + 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, # type: ignore + ), + 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, # type: ignore + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + 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}") + return app + + app = register_pages(1000) + + +def AppWithTenThousandPages(): + def register_pages(num): + from rxconfig import config # type: ignore + from typing import Tuple + + 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, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + 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, # type: ignore + ), + 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, # type: ignore + ), + align_items="left", + width="100%", + ), + ), + spacing="4", + ), + width="100%", + ) + + app = rx.App(state=rx.State) + + 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}") + return app + + app = register_pages(10000) + + +@pytest.fixture(scope="session") +def app_with_one_page( + 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(f"app1") + + yield AppHarness.create(root=root, app_source=AppWithOnePage) # type: ignore + + +@pytest.fixture(scope="session") +def app_with_ten_pages( + 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(f"app10") + + yield AppHarness.create(root=root, app_source=AppWithTenPages) # type: ignore + + +@pytest.fixture(scope="session") +def app_with_hundred_pages( + 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(f"app100") + + yield AppHarness.create(root=root, app_source=AppWithHundredPages) # type: ignore + + +@pytest.fixture(scope="session") +def app_with_thousand_pages( + 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(f"app1000") + + yield AppHarness.create(root=root, app_source=AppWithThousandPages) # type: ignore + + +@pytest.fixture(scope="session") +def app_with_ten_thousand_pages( + 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(f"app10000") + + yield AppHarness.create(root=root, app_source=AppWithTenThousandPages) # type: ignore + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1_compile_time_cold(benchmark, app_with_one_page): + def setup(): + with chdir(app_with_one_page.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1_compile_time_warm(benchmark, app_with_one_page): + def benchmark_fn(): + with chdir(app_with_one_page.app_path): + app_with_one_page.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10_compile_time_cold(benchmark, app_with_ten_pages): + def setup(): + with chdir(app_with_ten_pages.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10_compile_time_warm(benchmark, app_with_ten_pages): + def benchmark_fn(): + with chdir(app_with_ten_pages.app_path): + app_with_ten_pages.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_100_compile_time_cold(benchmark, app_with_hundred_pages): + def setup(): + with chdir(app_with_hundred_pages.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_100_compile_time_warm(benchmark, app_with_hundred_pages): + def benchmark_fn(): + with chdir(app_with_hundred_pages.app_path): + app_with_hundred_pages.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1000_compile_time_cold(benchmark, app_with_thousand_pages): + def setup(): + with chdir(app_with_thousand_pages.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1000_compile_time_warm(benchmark, app_with_thousand_pages): + def benchmark_fn(): + with chdir(app_with_thousand_pages.app_path): + app_with_thousand_pages.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10000_compile_time_cold(benchmark, app_with_ten_thousand_pages): + def setup(): + with chdir(app_with_ten_thousand_pages.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying page numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10000_compile_time_warm(benchmark, app_with_ten_thousand_pages): + def benchmark_fn(): + with chdir(app_with_ten_thousand_pages.app_path): + app_with_ten_thousand_pages.app_instance.compile_() + + benchmark(benchmark_fn) diff --git a/integration/benchmarks/test_medium_apps.py b/integration/benchmarks/test_medium_apps.py deleted file mode 100644 index 8798680995..0000000000 --- a/integration/benchmarks/test_medium_apps.py +++ /dev/null @@ -1,173 +0,0 @@ -"""Benchmark tests for medium sized apps.""" - -from __future__ import annotations - -import time -from typing import Generator - -import pytest - -from reflex.testing import AppHarness, chdir -from reflex.utils import build - - -def MediumApp(): - """Test that background tasks work as expected.""" - from rxconfig import config # type: ignore - from typing import Tuple - - 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, # type: ignore - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - 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, # type: ignore - ), - 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, # type: ignore - ), - align_items="left", - width="100%", - ), - ), - spacing="4", - ), - width="100%", - ) - - app = rx.App(state=rx.State) - - for i in range(1, 31): - if i % 2 == 1: - app.add_page(comp1, route=f"page{i}") - else: - app.add_page(comp2, route=f"page{i}") - - -@pytest.fixture(scope="session") -def medium_app( - 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(f"medium_app") - - yield AppHarness.create(root=root, app_source=MediumApp) # type: ignore - - -@pytest.mark.benchmark( - group="Medium sized app", - min_rounds=10, - timer=time.perf_counter, - disable_gc=True, - warmup=False, -) -def test_medium_app_compile_time_cold(benchmark, medium_app): - def setup(): - with chdir(medium_app.app_path): - medium_app._initialize_app() - build.setup_frontend(medium_app.app_path) - - def benchmark_fn(): - with chdir(medium_app.app_path): - medium_app.app_instance.compile_() - - benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) - - -@pytest.mark.benchmark( - group="Medium sized app", - min_rounds=10, - timer=time.perf_counter, - disable_gc=True, - warmup=False, -) -def test_medium_app_compile_time_warm(benchmark, medium_app): - def benchmark_fn(): - with chdir(medium_app.app_path): - medium_app.app_instance.compile_() - - benchmark(benchmark_fn) - - -# TODO: -def test_medium_app_hot_reload(): - pass From 5ea8cad3b5c4619ed2bcfa1e58d86bef7d9ed218 Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 8 Mar 2024 15:03:36 +0000 Subject: [PATCH 43/60] run black --- .../test_benchmark_compile_varying_pages.py | 714 +++++++++--------- 1 file changed, 350 insertions(+), 364 deletions(-) diff --git a/integration/benchmarks/test_benchmark_compile_varying_pages.py b/integration/benchmarks/test_benchmark_compile_varying_pages.py index 9cf0a0301a..c6b891325f 100644 --- a/integration/benchmarks/test_benchmark_compile_varying_pages.py +++ b/integration/benchmarks/test_benchmark_compile_varying_pages.py @@ -56,435 +56,421 @@ def index() -> rx.Component: def AppWithTenPages(): """Test that background tasks work as expected.""" - def register_pages(num): - from rxconfig import config # type: ignore - from typing import Tuple - - 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(), + from rxconfig import config # type: ignore + from typing import Tuple + + 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.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", + rx.select( + ["C", "PF", "SF", "PG", "SG"], + placeholder="Select a position. (All)", + on_change=State.set_position, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + size="3", ), - align="center", - spacing="7", - font_size="2em", ), - height="100vh", - ) - - comp2 = rx.vstack( - rx.hstack( + rx.vstack( rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", + rx.hstack( + rx.badge("Min Age: ", State.age[0]), + rx.divider(orientation="vertical"), + rx.badge("Max Age: ", State.age[1]), ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - size="3", + rx.slider( + default_value=[18, 50], + min=18, + max=50, + on_value_commit=State.set_age, # type: ignore ), + align_items="left", + width="100%", ), 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, # type: ignore - ), - align_items="left", - width="100%", + 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.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, # type: ignore - ), - align_items="left", - width="100%", + rx.slider( + default_value=[0, 25000000], + min=0, + max=25000000, + on_value_commit=State.set_salary, # type: ignore ), + align_items="left", + width="100%", ), - spacing="4", ), - width="100%", - ) - - app = rx.App(state=rx.State) + 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}") - return app + app = rx.App(state=rx.State) - app = register_pages(10) + for i in range(1, 11): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") def AppWithHundredPages(): - def register_pages(num): - from rxconfig import config # type: ignore - from typing import Tuple - - 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(), + from rxconfig import config # type: ignore + from typing import Tuple + + 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.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", + rx.select( + ["C", "PF", "SF", "PG", "SG"], + placeholder="Select a position. (All)", + on_change=State.set_position, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + size="3", ), - align="center", - spacing="7", - font_size="2em", ), - height="100vh", - ) - - comp2 = rx.vstack( - rx.hstack( + rx.vstack( rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", + rx.hstack( + rx.badge("Min Age: ", State.age[0]), + rx.divider(orientation="vertical"), + rx.badge("Max Age: ", State.age[1]), ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - size="3", + rx.slider( + default_value=[18, 50], + min=18, + max=50, + on_value_commit=State.set_age, # type: ignore ), + align_items="left", + width="100%", ), 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, # type: ignore - ), - align_items="left", - width="100%", + 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.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, # type: ignore - ), - align_items="left", - width="100%", + rx.slider( + default_value=[0, 25000000], + min=0, + max=25000000, + on_value_commit=State.set_salary, # type: ignore ), + align_items="left", + width="100%", ), - spacing="4", ), - width="100%", - ) - - app = rx.App(state=rx.State) + 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}") - return app + app = rx.App(state=rx.State) - app = register_pages(100) + for i in range(1, 101): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") + return app def AppWithThousandPages(): - def register_pages(num): - from rxconfig import config # type: ignore - from typing import Tuple - - 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(), + from rxconfig import config # type: ignore + from typing import Tuple + + 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.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", + rx.select( + ["C", "PF", "SF", "PG", "SG"], + placeholder="Select a position. (All)", + on_change=State.set_position, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + size="3", ), - align="center", - spacing="7", - font_size="2em", ), - height="100vh", - ) - - comp2 = rx.vstack( - rx.hstack( + rx.vstack( rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", + rx.hstack( + rx.badge("Min Age: ", State.age[0]), + rx.divider(orientation="vertical"), + rx.badge("Max Age: ", State.age[1]), ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - size="3", + rx.slider( + default_value=[18, 50], + min=18, + max=50, + on_value_commit=State.set_age, # type: ignore ), + align_items="left", + width="100%", ), 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, # type: ignore - ), - align_items="left", - width="100%", + 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.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, # type: ignore - ), - align_items="left", - width="100%", + rx.slider( + default_value=[0, 25000000], + min=0, + max=25000000, + on_value_commit=State.set_salary, # type: ignore ), + align_items="left", + width="100%", ), - spacing="4", ), - width="100%", - ) - - app = rx.App(state=rx.State) + 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}") - return app + app = rx.App(state=rx.State) - app = register_pages(1000) + for i in range(1, 1001): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") + return app def AppWithTenThousandPages(): - def register_pages(num): - from rxconfig import config # type: ignore - from typing import Tuple - - 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(), + from rxconfig import config # type: ignore + from typing import Tuple + + 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.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", + rx.select( + ["C", "PF", "SF", "PG", "SG"], + placeholder="Select a position. (All)", + on_change=State.set_position, # type: ignore + size="3", + ), + rx.select( + college, + placeholder="Select a college. (All)", + on_change=State.set_college, # type: ignore + size="3", ), - align="center", - spacing="7", - font_size="2em", ), - height="100vh", - ) - - comp2 = rx.vstack( - rx.hstack( + rx.vstack( rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", + rx.hstack( + rx.badge("Min Age: ", State.age[0]), + rx.divider(orientation="vertical"), + rx.badge("Max Age: ", State.age[1]), ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - size="3", + rx.slider( + default_value=[18, 50], + min=18, + max=50, + on_value_commit=State.set_age, # type: ignore ), + align_items="left", + width="100%", ), 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, # type: ignore - ), - align_items="left", - width="100%", + 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.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, # type: ignore - ), - align_items="left", - width="100%", + rx.slider( + default_value=[0, 25000000], + min=0, + max=25000000, + on_value_commit=State.set_salary, # type: ignore ), + align_items="left", + width="100%", ), - spacing="4", ), - width="100%", - ) - - app = rx.App(state=rx.State) + 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}") - return app + app = rx.App(state=rx.State) - app = register_pages(10000) + for i in range(1, 10001): + if i % 2 == 1: + app.add_page(comp1, route=f"page{i}") + else: + app.add_page(comp2, route=f"page{i}") @pytest.fixture(scope="session") From 4444702742c3bc42fbd1e3055f4c233159db422d Mon Sep 17 00:00:00 2001 From: Elijah Date: Fri, 8 Mar 2024 16:34:49 +0000 Subject: [PATCH 44/60] benchmark tests for varying component number --- ...ges.py => test_benchmark_compile_pages.py} | 0 .../test_benchmarks_compile_components.py | 486 ++++++++++++++++++ 2 files changed, 486 insertions(+) rename integration/benchmarks/{test_benchmark_compile_varying_pages.py => test_benchmark_compile_pages.py} (100%) create mode 100644 integration/benchmarks/test_benchmarks_compile_components.py diff --git a/integration/benchmarks/test_benchmark_compile_varying_pages.py b/integration/benchmarks/test_benchmark_compile_pages.py similarity index 100% rename from integration/benchmarks/test_benchmark_compile_varying_pages.py rename to integration/benchmarks/test_benchmark_compile_pages.py diff --git a/integration/benchmarks/test_benchmarks_compile_components.py b/integration/benchmarks/test_benchmarks_compile_components.py new file mode 100644 index 0000000000..e474568919 --- /dev/null +++ b/integration/benchmarks/test_benchmarks_compile_components.py @@ -0,0 +1,486 @@ +"""Benchmark tests for medium sized apps.""" + +from __future__ import annotations + +import time +from typing import Generator + +import pytest + +from reflex.testing import AppHarness, chdir +from reflex.utils import build +from reflex.utils import path_ops +from reflex import constants +from reflex.compiler import utils + + +def AppWithTenComponents(): + """Test that background tasks work as expected.""" + + from rxconfig import config # type: ignore + + import reflex as rx + + class State(rx.State): + """The app state.""" + + pass + + def render_component(): + 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"), + ), + ), + ) + ), + ) + + def index() -> rx.Component: + return rx.center(rx.vstack(render_component())) + + app = rx.App(state=rx.State) + app.add_page(index) + + +def AppWithHUndredComponentOnePage(): + """Test that background tasks work as expected.""" + + from rxconfig import config # type: ignore + from typing import Tuple + + import reflex as rx + + class State(rx.State): + """The app state.""" + + pass + + def render_component(): + 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"), + ), + ), + ) + ), + ) + ] * 10 + + def index() -> rx.Component: + return rx.center(rx.vstack(*render_component())) + + app = rx.App(state=rx.State) + app.add_page(index) + + +def AppWithThousandComponentsOnePage(): + from rxconfig import config # type: ignore + from typing import Tuple + + import reflex as rx + + class State(rx.State): + """The app state.""" + + pass + + def render_component(): + 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"), + ), + ), + ) + ), + ) + ] * 100 + + def index() -> rx.Component: + return rx.center(rx.vstack(*render_component())) + + 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=AppWithTenComponents) # type: ignore + + +@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=AppWithHUndredComponentOnePage) # type: ignore + + +@pytest.fixture(scope="session") +def app_with_1000_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("app1000components") + + yield AppHarness.create(root=root, app_source=AppWithThousandComponentsOnePage) # type: ignore + + +@pytest.mark.benchmark( + group="Compile time of varying component numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10_compile_time_cold(benchmark, app_with_10_components): + def setup(): + with chdir(app_with_10_components.app_path): + utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_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=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_10_compile_time_warm(benchmark, app_with_10_components): + def benchmark_fn(): + with chdir(app_with_10_components.app_path): + app_with_10_components.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying component numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_100_compile_time_cold(benchmark, app_with_100_components): + def setup(): + with chdir(app_with_100_components.app_path): + utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying component numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_100_compile_time_warm(benchmark, app_with_100_components): + def benchmark_fn(): + with chdir(app_with_100_components.app_path): + app_with_100_components.app_instance.compile_() + + benchmark(benchmark_fn) + + +@pytest.mark.benchmark( + group="Compile time of varying component numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1000_compile_time_cold(benchmark, app_with_1000_components): + def setup(): + with chdir(app_with_1000_components.app_path): + utils.empty_dir(constants.Dirs.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=10) + + +@pytest.mark.benchmark( + group="Compile time of varying component numbers", + min_rounds=10, + timer=time.perf_counter, + disable_gc=True, + warmup=False, +) +def test_app_1000_compile_time_warm(benchmark, app_with_1000_components): + def benchmark_fn(): + with chdir(app_with_1000_components.app_path): + app_with_1000_components.app_instance.compile_() + + benchmark(benchmark_fn) From 2de72876688da2a0c53acb6890bc09878df2bb3f Mon Sep 17 00:00:00 2001 From: Martin Xu <15661672+martinxu9@users.noreply.github.com> Date: Fri, 8 Mar 2024 12:17:21 -0800 Subject: [PATCH 45/60] move benmark to its own folder --- .github/workflows/benchmarks.yml | 6 +++--- .github/workflows/integration_app_harness.yml | 2 +- benchmarks/__init__.py | 0 benchmarks/conftest.py | 20 +++++++++++++++++++ .../test_benchmark_compile_pages.py | 17 ++++++++-------- .../test_benchmarks_compile_components.py | 14 ++----------- .../test_compile_benchmark.py | 0 7 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 benchmarks/__init__.py create mode 100644 benchmarks/conftest.py rename {integration/benchmarks => benchmarks}/test_benchmark_compile_pages.py (99%) rename {integration/benchmarks => benchmarks}/test_benchmarks_compile_components.py (98%) rename {integration/benchmarks => benchmarks}/test_compile_benchmark.py (100%) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 4bdbb0c8cb..4af87765fd 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -108,14 +108,14 @@ jobs: python-version: ${{ matrix.python-version }} run-poetry-install: true create-venv-at-path: .venv - - name: Install additional dependencies for appharness test and DB - run: poetry run pip install pyvirtualdisplay pillow psycopg2-binary + - name: Install additional dependencies for DB access + run: poetry run pip install psycopg2-binary - name: Run benchmark tests env: APP_HARNESS_HEADLESS: 1 PYTHONUNBUFFERED: 1 run: | - poetry run pytest -v integration/benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} + poetry run pytest -v benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} diff --git a/.github/workflows/integration_app_harness.yml b/.github/workflows/integration_app_harness.yml index f3aaf4f54d..ed74ac39db 100644 --- a/.github/workflows/integration_app_harness.yml +++ b/.github/workflows/integration_app_harness.yml @@ -47,7 +47,7 @@ jobs: SCREENSHOT_DIR: /tmp/screenshots REDIS_URL: ${{ matrix.state_manager == 'redis' && 'redis://localhost:6379' || '' }} run: | - poetry run pytest integration --ignore=integration/benchmarks/ + poetry run pytest integration - uses: actions/upload-artifact@v4 name: Upload failed test screenshots if: always() diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/benchmarks/conftest.py b/benchmarks/conftest.py new file mode 100644 index 0000000000..11e3bd4d78 --- /dev/null +++ b/benchmarks/conftest.py @@ -0,0 +1,20 @@ +"""Shared conftest for all benchmark tests.""" + +import pytest + +from reflex.testing import AppHarness, AppHarnessProd + + +@pytest.fixture( + scope="session", params=[AppHarness, AppHarnessProd], ids=["dev", "prod"] +) +def app_harness_env(request): + """Parametrize the AppHarness class to use for the test, either dev or prod. + + Args: + request: The pytest fixture request object. + + Returns: + The AppHarness class to use for the test. + """ + return request.param diff --git a/integration/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py similarity index 99% rename from integration/benchmarks/test_benchmark_compile_pages.py rename to benchmarks/test_benchmark_compile_pages.py index c6b891325f..f906dbe08d 100644 --- a/integration/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -7,16 +7,14 @@ import pytest -from reflex.testing import AppHarness, chdir -from reflex.utils import build -from reflex.utils import path_ops from reflex import constants from reflex.compiler import utils +from reflex.testing import AppHarness, chdir +from reflex.utils import build def AppWithOnePage(): """Test that background tasks work as expected.""" - from rxconfig import config # type: ignore import reflex as rx @@ -55,9 +53,9 @@ def index() -> rx.Component: def AppWithTenPages(): """Test that background tasks work as expected.""" + from typing import Tuple from rxconfig import config # type: ignore - from typing import Tuple import reflex as rx @@ -160,9 +158,10 @@ class State(rx.State): def AppWithHundredPages(): - from rxconfig import config # type: ignore from typing import Tuple + from rxconfig import config # type: ignore + import reflex as rx docs_url = "https://reflex.dev/docs/getting-started/introduction/" @@ -265,9 +264,10 @@ class State(rx.State): def AppWithThousandPages(): - from rxconfig import config # type: ignore from typing import Tuple + from rxconfig import config # type: ignore + import reflex as rx docs_url = "https://reflex.dev/docs/getting-started/introduction/" @@ -370,9 +370,10 @@ class State(rx.State): def AppWithTenThousandPages(): - from rxconfig import config # type: ignore from typing import Tuple + from rxconfig import config # type: ignore + import reflex as rx docs_url = "https://reflex.dev/docs/getting-started/introduction/" diff --git a/integration/benchmarks/test_benchmarks_compile_components.py b/benchmarks/test_benchmarks_compile_components.py similarity index 98% rename from integration/benchmarks/test_benchmarks_compile_components.py rename to benchmarks/test_benchmarks_compile_components.py index e474568919..a60bd078f4 100644 --- a/integration/benchmarks/test_benchmarks_compile_components.py +++ b/benchmarks/test_benchmarks_compile_components.py @@ -7,18 +7,14 @@ import pytest -from reflex.testing import AppHarness, chdir -from reflex.utils import build -from reflex.utils import path_ops from reflex import constants from reflex.compiler import utils +from reflex.testing import AppHarness, chdir +from reflex.utils import build def AppWithTenComponents(): """Test that background tasks work as expected.""" - - from rxconfig import config # type: ignore - import reflex as rx class State(rx.State): @@ -119,10 +115,6 @@ def index() -> rx.Component: def AppWithHUndredComponentOnePage(): """Test that background tasks work as expected.""" - - from rxconfig import config # type: ignore - from typing import Tuple - import reflex as rx class State(rx.State): @@ -224,8 +216,6 @@ def index() -> rx.Component: def AppWithThousandComponentsOnePage(): - from rxconfig import config # type: ignore - from typing import Tuple import reflex as rx diff --git a/integration/benchmarks/test_compile_benchmark.py b/benchmarks/test_compile_benchmark.py similarity index 100% rename from integration/benchmarks/test_compile_benchmark.py rename to benchmarks/test_compile_benchmark.py From c44ab39d48c30f64e7dcb888514a4b3e519da3b7 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 10:38:43 +0000 Subject: [PATCH 46/60] refactor dups --- benchmarks/__init__.py | 1 + ...y => test_benchmark_compile_components.py} | 275 +++-------- benchmarks/test_benchmark_compile_pages.py | 458 +++++------------- reflex/testing.py | 22 +- 4 files changed, 229 insertions(+), 527 deletions(-) rename benchmarks/{test_benchmarks_compile_components.py => test_benchmark_compile_components.py} (50%) diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py index e69de29bb2..f892e5c48c 100644 --- a/benchmarks/__init__.py +++ b/benchmarks/__init__.py @@ -0,0 +1 @@ +"""Reflex benchmarks.""" diff --git a/benchmarks/test_benchmarks_compile_components.py b/benchmarks/test_benchmark_compile_components.py similarity index 50% rename from benchmarks/test_benchmarks_compile_components.py rename to benchmarks/test_benchmark_compile_components.py index a60bd078f4..2fad849e3a 100644 --- a/benchmarks/test_benchmarks_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -2,6 +2,7 @@ from __future__ import annotations +import functools import time from typing import Generator @@ -13,22 +14,17 @@ from reflex.utils import build -def AppWithTenComponents(): +def render_component(num: int): """Test that background tasks work as expected.""" import reflex as rx - class State(rx.State): - """The app state.""" - - pass - - def render_component(): - return rx.fragment( + return [ + rx.fragment( rx.box( rx.accordion.root( rx.accordion.item( - header="Full Ingredients", - content="Yes. It's built with accessibility in mind.", + header="Full Ingredients", # type: ignore + content="Yes. It's built with accessibility in mind.", # type: ignore font_size="3em", ), rx.accordion.item( @@ -105,9 +101,15 @@ def render_component(): ) ), ) + ] * num + + +def AppWithTenComponents(): + """Test that background tasks work as expected.""" + import reflex as rx def index() -> rx.Component: - return rx.center(rx.vstack(render_component())) + return rx.center(rx.vstack(*render_component(1))) app = rx.App(state=rx.State) app.add_page(index) @@ -117,201 +119,19 @@ def AppWithHUndredComponentOnePage(): """Test that background tasks work as expected.""" import reflex as rx - class State(rx.State): - """The app state.""" - - pass - - def render_component(): - 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"), - ), - ), - ) - ), - ) - ] * 10 - def index() -> rx.Component: - return rx.center(rx.vstack(*render_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 1000 components on a page.""" import reflex as rx - class State(rx.State): - """The app state.""" - - pass - - def render_component(): - 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"), - ), - ), - ) - ), - ) - ] * 100 - def index() -> rx.Component: - return rx.center(rx.vstack(*render_component())) + return rx.center(rx.vstack(*render_component(10))) app = rx.App(state=rx.State) app.add_page(index) @@ -331,7 +151,12 @@ def app_with_10_components( """ root = tmp_path_factory.mktemp("app10components") - yield AppHarness.create(root=root, app_source=AppWithTenComponents) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithTenComponents, render_component=render_component # type: ignore + ), + ) # type: ignore @pytest.fixture(scope="session") @@ -348,7 +173,12 @@ def app_with_100_components( """ root = tmp_path_factory.mktemp("app100components") - yield AppHarness.create(root=root, app_source=AppWithHUndredComponentOnePage) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithHUndredComponentOnePage, render_component=render_component # type: ignore + ), + ) # type: ignore @pytest.fixture(scope="session") @@ -365,7 +195,12 @@ def app_with_1000_components( """ root = tmp_path_factory.mktemp("app1000components") - yield AppHarness.create(root=root, app_source=AppWithThousandComponentsOnePage) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithThousandComponentsOnePage, render_component=render_component # type: ignore + ), + ) # type: ignore @pytest.mark.benchmark( @@ -376,6 +211,13 @@ def app_with_1000_components( 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -397,6 +239,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_10_components.app_path): app_with_10_components.app_instance.compile_() @@ -412,6 +261,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -433,6 +289,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_100_components.app_path): app_with_100_components.app_instance.compile_() @@ -448,6 +311,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -469,6 +339,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_1000_components.app_path): app_with_1000_components.app_instance.compile_() diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index f906dbe08d..c57e6a2b39 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -2,6 +2,7 @@ from __future__ import annotations +import functools import time from typing import Generator @@ -13,46 +14,14 @@ from reflex.utils import build -def AppWithOnePage(): - """Test that background tasks work as expected.""" - from rxconfig import config # type: ignore - - 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.chakra.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 render_multiple_pages(app, num: int): + """Add multiple pages based on num. + Args: + app: The App object. + num: number of pages to render. -def AppWithTenPages(): - """Test that background tasks work as expected.""" + """ from typing import Tuple from rxconfig import config # type: ignore @@ -148,330 +117,81 @@ class State(rx.State): width="100%", ) - app = rx.App(state=rx.State) - - for i in range(1, 11): + 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 AppWithHundredPages(): - from typing import Tuple - +def AppWithOnePage(): + """Test that background tasks work as expected.""" from rxconfig import config # type: ignore 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", - ) + pass - comp2 = rx.vstack( - rx.hstack( - rx.vstack( - rx.select( - ["C", "PF", "SF", "PG", "SG"], - placeholder="Select a position. (All)", - on_change=State.set_position, # type: ignore - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - size="3", - ), + def index() -> rx.Component: + return rx.center( + rx.chakra.input( + id="token", value=State.router.session.client_token, is_read_only=True ), 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, # type: ignore - ), - 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, # type: ignore - ), - align_items="left", - width="100%", + 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", ), - spacing="4", - ), - width="100%", - ) + height="100vh", + ) app = rx.App(state=rx.State) + app.add_page(index) - for i in range(1, 101): - if i % 2 == 1: - app.add_page(comp1, route=f"page{i}") - else: - app.add_page(comp2, route=f"page{i}") - return app +def AppWithTenPages(): + """Test that background tasks work as expected.""" + import reflex as rx -def AppWithThousandPages(): - from typing import Tuple + app = rx.App(state=rx.State) + render_multiple_pages(app, 10) - from rxconfig import config # type: ignore +def AppWithHundredPages(): + """A reflex app with 100 pages.""" 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) + app = rx.App(state=rx.State) + render_multiple_pages(app, 100) - 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, # type: ignore - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - 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, # type: ignore - ), - 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, # type: ignore - ), - align_items="left", - width="100%", - ), - ), - spacing="4", - ), - width="100%", - ) +def AppWithThousandPages(): + """A reflex app with Thousand pages.""" + import reflex as rx app = rx.App(state=rx.State) - - for i in range(1, 1001): - if i % 2 == 1: - app.add_page(comp1, route=f"page{i}") - else: - app.add_page(comp2, route=f"page{i}") - return app + render_multiple_pages(app, 1000) def AppWithTenThousandPages(): - from typing import Tuple - - from rxconfig import config # type: ignore - + """A reflex app with ten thousand pages.""" 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, # type: ignore - size="3", - ), - rx.select( - college, - placeholder="Select a college. (All)", - on_change=State.set_college, # type: ignore - 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, # type: ignore - ), - 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, # type: ignore - ), - align_items="left", - width="100%", - ), - ), - spacing="4", - ), - width="100%", - ) - app = rx.App(state=rx.State) - - for i in range(1, 10001): - if i % 2 == 1: - app.add_page(comp1, route=f"page{i}") - else: - app.add_page(comp2, route=f"page{i}") + render_multiple_pages(app, 10000) @pytest.fixture(scope="session") @@ -504,8 +224,7 @@ def app_with_ten_pages( running AppHarness instance """ root = tmp_path_factory.mktemp(f"app10") - - yield AppHarness.create(root=root, app_source=AppWithTenPages) # type: ignore + yield AppHarness.create(root=root, app_source=functools.partial(AppWithTenPages, render_comp=render_multiple_pages)) # type: ignore @pytest.fixture(scope="session") @@ -522,7 +241,12 @@ def app_with_hundred_pages( """ root = tmp_path_factory.mktemp(f"app100") - yield AppHarness.create(root=root, app_source=AppWithHundredPages) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithHundredPages, render_comp=render_multiple_pages # type: ignore + ), + ) # type: ignore @pytest.fixture(scope="session") @@ -539,7 +263,12 @@ def app_with_thousand_pages( """ root = tmp_path_factory.mktemp(f"app1000") - yield AppHarness.create(root=root, app_source=AppWithThousandPages) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithThousandPages, render_comp=render_multiple_pages + ), + ) # type: ignore @pytest.fixture(scope="session") @@ -556,7 +285,12 @@ def app_with_ten_thousand_pages( """ root = tmp_path_factory.mktemp(f"app10000") - yield AppHarness.create(root=root, app_source=AppWithTenThousandPages) # type: ignore + yield AppHarness.create( + root=root, + app_source=functools.partial( + AppWithTenThousandPages, render_comp=render_multiple_pages + ), + ) # type: ignore @pytest.mark.benchmark( @@ -567,6 +301,13 @@ def app_with_ten_thousand_pages( 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -588,6 +329,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_one_page.app_path): app_with_one_page.app_instance.compile_() @@ -603,6 +351,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -624,6 +379,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_ten_pages.app_path): app_with_ten_pages.app_instance.compile_() @@ -639,6 +401,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -660,6 +429,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_hundred_pages.app_path): app_with_hundred_pages.app_instance.compile_() @@ -675,6 +451,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -696,6 +479,13 @@ def benchmark_fn(): 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. + """ + def benchmark_fn(): with chdir(app_with_thousand_pages.app_path): app_with_thousand_pages.app_instance.compile_() @@ -711,6 +501,13 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) @@ -732,6 +529,13 @@ def benchmark_fn(): 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_() diff --git a/reflex/testing.py b/reflex/testing.py index dadce40a55..a189bb7587 100644 --- a/reflex/testing.py +++ b/reflex/testing.py @@ -208,7 +208,9 @@ def _initialize_app(self): # get the source from a function or module object source_code = "\n".join( [ - "\n".join(f"{k} = {v!r}" for k, v in app_globals.items()), + "\n".join( + self.get_app_global_source(k, v) for k, v in app_globals.items() + ), self._get_source_from_app_source(self.app_source), ] ) @@ -327,6 +329,24 @@ def start(self) -> "AppHarness": self._wait_frontend() return self + @staticmethod + def get_app_global_source(key, value): + """Get the source code of a global object. + If value is a function or class we render the actual + source of value otherwise we assign value to key. + + Args: + key: variable name to assign value to. + value: value of the global variable. + + Returns: + The rendered app global code. + + """ + if not inspect.isclass(value) and not inspect.isfunction(value): + return f"{key} = {value!r}" + return inspect.getsource(value) + def __enter__(self) -> "AppHarness": """Contextmanager protocol for `start()`. From 060a8f349c644db34e8b1a278c8345ac05df2ff2 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 12:52:37 +0000 Subject: [PATCH 47/60] test reducing number of benchmark rounds --- .../test_benchmark_compile_components.py | 13 ++++------ benchmarks/test_benchmark_compile_pages.py | 25 ++++++++----------- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index 2fad849e3a..2b8ab63e58 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -205,7 +205,6 @@ def app_with_1000_components( @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -233,7 +232,7 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -255,7 +254,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -278,12 +276,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -305,7 +303,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -328,12 +325,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying component numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index c57e6a2b39..fcb3bc4956 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -295,7 +295,6 @@ def app_with_ten_thousand_pages( @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -318,12 +317,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -345,7 +344,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -368,12 +366,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -395,7 +393,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -418,12 +415,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -445,7 +442,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -468,12 +464,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, + min_rounds=2, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -495,7 +491,6 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -518,12 +513,12 @@ 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=10, + min_rounds=2, timer=time.perf_counter, disable_gc=True, warmup=False, From 96b522479d9e093cd34cf6ed99d97ce17fe95abc Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 13:20:59 +0000 Subject: [PATCH 48/60] add more output to debug CI hang --- .github/workflows/benchmarks.yml | 2 +- benchmarks/test_benchmark_compile_pages.py | 94 +++++++++++----------- 2 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 4af87765fd..16c8ab7fd9 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -115,7 +115,7 @@ jobs: APP_HARNESS_HEADLESS: 1 PYTHONUNBUFFERED: 1 run: | - poetry run pytest -v benchmarks/ --benchmark-json=${{ env.OUTPUT_FILE }} + 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. if: ${{ env.DATABASE_URL }} diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index fcb3bc4956..4c44904d4d 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -489,50 +489,50 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.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) - - -@pytest.mark.benchmark( - group="Compile time of varying page numbers", - min_rounds=2, - 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) +# @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(constants.Dirs.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) +# +# +# @pytest.mark.benchmark( +# group="Compile time of varying page numbers", +# min_rounds=2, +# 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) From ac70fd394c337e1055867f10da1d4c71f2badc1f Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 14:12:10 +0000 Subject: [PATCH 49/60] experiment timeout minutes for windows --- .github/workflows/benchmarks.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 16c8ab7fd9..041940e94a 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -77,7 +77,7 @@ jobs: simple-apps-benchmarks: env: OUTPUT_FILE: benchmarks.json - timeout-minutes: 30 + timeout-minutes: 50 strategy: # Prioritize getting more information out of the workflow (even if something fails) fail-fast: false From 85b71a045108b6887175db73ae800a6def7d6d9a Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 14:16:13 +0000 Subject: [PATCH 50/60] experiment timeout minutes for windows --- benchmarks/test_benchmark_compile_components.py | 13 ++++++++++--- benchmarks/test_benchmark_compile_pages.py | 6 +++--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index 2b8ab63e58..aae952d1a1 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -15,7 +15,14 @@ def render_component(num: int): - """Test that background tasks work as expected.""" + """Test that background tasks work as expected. + + Args: + num: number of components to produce. + + Returns: + The rendered number of components. + """ import reflex as rx return [ @@ -28,8 +35,8 @@ def render_component(num: int): font_size="3em", ), rx.accordion.item( - header="Applications", - content="Yes. It's unstyled by default, giving you freedom over the look and feel.", + header="Applications", # type: ignore + content="Yes. It's unstyled by default, giving you freedom over the look and feel.", # type: ignore ), collapsible=True, variant="ghost", diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index 4c44904d4d..9f6aa65c17 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -265,8 +265,8 @@ def app_with_thousand_pages( yield AppHarness.create( root=root, - app_source=functools.partial( - AppWithThousandPages, render_comp=render_multiple_pages + app_source=functools.partial( # type: ignore + AppWithThousandPages, render_comp=render_multiple_pages # type: ignore ), ) # type: ignore @@ -288,7 +288,7 @@ def app_with_ten_thousand_pages( yield AppHarness.create( root=root, app_source=functools.partial( - AppWithTenThousandPages, render_comp=render_multiple_pages + AppWithTenThousandPages, render_comp=render_multiple_pages # type: ignore ), ) # type: ignore From 86217f547d34b5b3a26c7e83db3950c9ba52c056 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 15:09:06 +0000 Subject: [PATCH 51/60] skip cold tests on windows --- .../test_benchmark_compile_components.py | 151 ++++++------ benchmarks/test_benchmark_compile_pages.py | 216 +++++++++--------- 2 files changed, 186 insertions(+), 181 deletions(-) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index aae952d1a1..93e1d0f145 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -210,31 +210,86 @@ def app_with_1000_components( ) # type: ignore -@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. +if not constants.IS_WINDOWS: + # For now skip cold tests on windows since it takes too much time as a result + # of npm. + @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(constants.Dirs.WEB_PAGES, keep_files=["_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", + 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(constants.Dirs.WEB_PAGES, keep_files=["_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", + 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_10_components: The app harness. - """ + Args: + benchmark: The benchmark fixture. + app_with_1000_components: The app harness. + """ - def setup(): - with chdir(app_with_10_components.app_path): - utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) - app_with_10_components._initialize_app() - build.setup_frontend(app_with_10_components.app_path) + def setup(): + with chdir(app_with_1000_components.app_path): + utils.empty_dir(constants.Dirs.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_10_components.app_path): - app_with_10_components.app_instance.compile_() + 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=10) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=5) @pytest.mark.benchmark( @@ -259,33 +314,6 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.WEB_PAGES, keep_files=["_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, @@ -308,33 +336,6 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.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, diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index 9f6aa65c17..cae7a9c0ff 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -293,31 +293,116 @@ def app_with_ten_thousand_pages( ) # type: ignore -@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(constants.Dirs.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) +if not constants.IS_WINDOWS: + # For now skip cold tests on windows since it takes too much time as a result + # of npm. + @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(constants.Dirs.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) + + @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(constants.Dirs.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) + + @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( + constants.Dirs.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) + + @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( + constants.Dirs.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) @pytest.mark.benchmark( @@ -342,33 +427,6 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.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) - - @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=5, @@ -391,33 +449,6 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.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) - - @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=5, @@ -440,33 +471,6 @@ def benchmark_fn(): benchmark(benchmark_fn) -@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(constants.Dirs.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) - - @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=2, From 5b208337ae06c7fffeaa59f7828d27b354b59288 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 15:36:41 +0000 Subject: [PATCH 52/60] fix broken tests --- benchmarks/test_benchmark_compile_components.py | 9 +++++++++ benchmarks/test_benchmark_compile_pages.py | 12 ++++++++++++ 2 files changed, 21 insertions(+) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index 93e1d0f145..9dff97c4ed 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -306,6 +306,9 @@ def test_app_10_compile_time_warm(benchmark, app_with_10_components): 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): @@ -328,6 +331,9 @@ def test_app_100_compile_time_warm(benchmark, app_with_100_components): 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): @@ -350,6 +356,9 @@ def test_app_1000_compile_time_warm(benchmark, app_with_1000_components): 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): diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index cae7a9c0ff..a89097ef00 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -419,6 +419,9 @@ def test_app_1_compile_time_warm(benchmark, app_with_one_page): 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): @@ -441,6 +444,9 @@ def test_app_10_compile_time_warm(benchmark, app_with_ten_pages): 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): @@ -463,6 +469,9 @@ def test_app_100_compile_time_warm(benchmark, app_with_hundred_pages): 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): @@ -485,6 +494,9 @@ def test_app_1000_compile_time_warm(benchmark, app_with_thousand_pages): 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): From 0d646101f81b67ff5c70d6c8bd2073ae2caab1f3 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 15:58:22 +0000 Subject: [PATCH 53/60] Increase rounds of 1000 pages warm test to 5 --- benchmarks/test_benchmark_compile_pages.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index a89097ef00..60b0341899 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -482,7 +482,7 @@ def benchmark_fn(): @pytest.mark.benchmark( group="Compile time of varying page numbers", - min_rounds=2, + min_rounds=5, timer=time.perf_counter, disable_gc=True, warmup=False, @@ -504,6 +504,7 @@ def benchmark_fn(): benchmark(benchmark_fn) +# Temporarily disabling this test since it takes a lot of time to run and fails CI as result. # @pytest.mark.benchmark( # group="Compile time of varying page numbers", From 136fc0114be2392476d83f82a60edfba5f8cb5b6 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 16:01:17 +0000 Subject: [PATCH 54/60] precommit fix --- benchmarks/test_benchmark_compile_pages.py | 1 + 1 file changed, 1 insertion(+) diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index 60b0341899..f3982ad21f 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -504,6 +504,7 @@ def benchmark_fn(): benchmark(benchmark_fn) + # Temporarily disabling this test since it takes a lot of time to run and fails CI as result. # @pytest.mark.benchmark( From cbe293df40906c516ab9e8931c88579cc1749009 Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 16:20:32 +0000 Subject: [PATCH 55/60] remove old benchmarks --- benchmarks/test_compile_benchmark.py | 121 --------------------------- 1 file changed, 121 deletions(-) delete mode 100644 benchmarks/test_compile_benchmark.py diff --git a/benchmarks/test_compile_benchmark.py b/benchmarks/test_compile_benchmark.py deleted file mode 100644 index 85815bae08..0000000000 --- a/benchmarks/test_compile_benchmark.py +++ /dev/null @@ -1,121 +0,0 @@ -"""Benchmark the time it takes to compile a reflex app.""" - -import importlib - -import reflex - -rx = reflex - - -class State(rx.State): - """A simple state class with a count variable.""" - - count: int = 0 - - def increment(self): - """Increment the count.""" - self.count += 1 - - def decrement(self): - """Decrement the count.""" - self.count -= 1 - - -class SliderVariation(State): - """A simple state class with a count variable.""" - - value: int = 50 - - def set_end(self, value: int): - """Increment the count. - - Args: - value: The value of the slider. - """ - self.value = value - - -def sample_small_page() -> rx.Component: - """A simple page with a button that increments the count. - - Returns: - A reflex component. - """ - return rx.vstack( - *[rx.button(State.count, font_size="2em") for i in range(100)], - gap="1em", - ) - - -def sample_large_page() -> rx.Component: - """A large page with a slider that increments the count. - - Returns: - A reflex component. - """ - return rx.vstack( - *[ - rx.vstack( - rx.heading(SliderVariation.value), - rx.slider(on_change_end=SliderVariation.set_end), - width="100%", - ) - for i in range(100) - ], - gap="1em", - ) - - -def add_small_pages(app: rx.App): - """Add 10 small pages to the app. - - Args: - app: The reflex app to add the pages to. - """ - for i in range(10): - app.add_page(sample_small_page, route=f"/{i}") - - -def add_large_pages(app: rx.App): - """Add 10 large pages to the app. - - Args: - app: The reflex app to add the pages to. - """ - for i in range(10): - app.add_page(sample_large_page, route=f"/{i}") - - -def test_mean_import_time(benchmark): - """Test that the mean import time is less than 1 second. - - Args: - benchmark: The benchmark fixture. - """ - - def import_reflex(): - importlib.reload(reflex) - - # Benchmark the import - benchmark(import_reflex) - - -def test_mean_add_small_page_time(benchmark): - """Test that the mean add page time is less than 1 second. - - Args: - benchmark: The benchmark fixture. - """ - app = rx.App(state=State) - benchmark(add_small_pages, app) - - -def test_mean_add_large_page_time(benchmark): - """Test that the mean add page time is less than 1 second. - - Args: - benchmark: The benchmark fixture. - """ - app = rx.App(state=State) - results = benchmark(add_large_pages, app) - print(results) From f4f8a8a2a2eb561f472f516d9ee1a93a4254f8ec Mon Sep 17 00:00:00 2001 From: Elijah Date: Mon, 11 Mar 2024 17:14:20 +0000 Subject: [PATCH 56/60] refactor --- benchmarks/__init__.py | 2 + .../test_benchmark_compile_components.py | 155 ++++----- benchmarks/test_benchmark_compile_pages.py | 319 +++++++++--------- 3 files changed, 241 insertions(+), 235 deletions(-) diff --git a/benchmarks/__init__.py b/benchmarks/__init__.py index f892e5c48c..cef840e3ed 100644 --- a/benchmarks/__init__.py +++ b/benchmarks/__init__.py @@ -1 +1,3 @@ """Reflex benchmarks.""" + +WINDOWS_SKIP_REASON = "Takes too much time as a result of npm" diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index 9dff97c4ed..adacefac49 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -8,6 +8,7 @@ import pytest +from benchmarks import WINDOWS_SKIP_REASON from reflex import constants from reflex.compiler import utils from reflex.testing import AppHarness, chdir @@ -210,86 +211,32 @@ def app_with_1000_components( ) # type: ignore -if not constants.IS_WINDOWS: - # For now skip cold tests on windows since it takes too much time as a result - # of npm. - @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(constants.Dirs.WEB_PAGES, keep_files=["_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", - 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(constants.Dirs.WEB_PAGES, keep_files=["_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", - 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. +@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_1000_components: The app harness. - """ + Args: + benchmark: The benchmark fixture. + app_with_10_components: The app harness. + """ - def setup(): - with chdir(app_with_1000_components.app_path): - utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) - app_with_1000_components._initialize_app() - build.setup_frontend(app_with_1000_components.app_path) + def setup(): + with chdir(app_with_10_components.app_path): + utils.empty_dir(constants.Dirs.WEB_PAGES, keep_files=["_app.js"]) + app_with_10_components._initialize_app() + build.setup_frontend(app_with_10_components.app_path) - def benchmark_fn(): - with chdir(app_with_1000_components.app_path): - app_with_1000_components.app_instance.compile_() + 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=5) + benchmark.pedantic(benchmark_fn, setup=setup, rounds=10) @pytest.mark.benchmark( @@ -317,6 +264,34 @@ def benchmark_fn(): 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(constants.Dirs.WEB_PAGES, keep_files=["_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, @@ -342,6 +317,34 @@ def benchmark_fn(): 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(constants.Dirs.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, diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index f3982ad21f..ac631a6758 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -8,6 +8,7 @@ import pytest +from benchmarks import WINDOWS_SKIP_REASON from reflex import constants from reflex.compiler import utils from reflex.testing import AppHarness, chdir @@ -293,116 +294,32 @@ def app_with_ten_thousand_pages( ) # type: ignore -if not constants.IS_WINDOWS: - # For now skip cold tests on windows since it takes too much time as a result - # of npm. - @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(constants.Dirs.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) - - @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(constants.Dirs.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) - - @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( - constants.Dirs.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) - - @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( - constants.Dirs.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) +@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(constants.Dirs.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) @pytest.mark.benchmark( @@ -430,6 +347,34 @@ def benchmark_fn(): benchmark(benchmark_fn) +@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(constants.Dirs.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) + + @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=5, @@ -455,6 +400,34 @@ def benchmark_fn(): benchmark(benchmark_fn) +@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(constants.Dirs.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) + + @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=5, @@ -480,6 +453,34 @@ def benchmark_fn(): benchmark(benchmark_fn) +@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(constants.Dirs.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) + + @pytest.mark.benchmark( group="Compile time of varying page numbers", min_rounds=5, @@ -505,52 +506,52 @@ def benchmark_fn(): benchmark(benchmark_fn) -# Temporarily disabling this test since it takes a lot of time to run and fails CI as result. - -# @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(constants.Dirs.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) -# -# -# @pytest.mark.benchmark( -# group="Compile time of varying page numbers", -# min_rounds=2, -# 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) +@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(constants.Dirs.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) + + +@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) From 16ca965c772830d6b420b26d8ee3b7a4e13fcb1e Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 12 Mar 2024 09:59:30 +0000 Subject: [PATCH 57/60] fixed docstrings --- .../test_benchmark_compile_components.py | 18 +++++++------- benchmarks/test_benchmark_compile_pages.py | 24 +++++++++---------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index adacefac49..6b0d50cd31 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -1,4 +1,4 @@ -"""Benchmark tests for medium sized apps.""" +"""Benchmark tests for apps with varying component numbers.""" from __future__ import annotations @@ -16,7 +16,7 @@ def render_component(num: int): - """Test that background tasks work as expected. + """Generate a number of components based on num. Args: num: number of components to produce. @@ -112,8 +112,8 @@ def render_component(num: int): ] * num -def AppWithTenComponents(): - """Test that background tasks work as expected.""" +def AppWithTenComponentsOnePage(): + """A reflex app with roughly 10 components on one page.""" import reflex as rx def index() -> rx.Component: @@ -124,7 +124,7 @@ def index() -> rx.Component: def AppWithHUndredComponentOnePage(): - """Test that background tasks work as expected.""" + """A reflex app with roughly 100 components on one page.""" import reflex as rx def index() -> rx.Component: @@ -135,7 +135,7 @@ def index() -> rx.Component: def AppWithThousandComponentsOnePage(): - """A reflex app with 1000 components on a page.""" + """A reflex app with roughly 1000 components on one page.""" import reflex as rx def index() -> rx.Component: @@ -162,7 +162,7 @@ def app_with_10_components( yield AppHarness.create( root=root, app_source=functools.partial( - AppWithTenComponents, render_component=render_component # type: ignore + AppWithTenComponentsOnePage, render_component=render_component # type: ignore ), ) # type: ignore @@ -193,13 +193,13 @@ def app_with_100_components( def app_with_1000_components( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 1000 components at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: - running AppHarness instance + an AppHarness instance """ root = tmp_path_factory.mktemp("app1000components") diff --git a/benchmarks/test_benchmark_compile_pages.py b/benchmarks/test_benchmark_compile_pages.py index ac631a6758..c742ed8438 100644 --- a/benchmarks/test_benchmark_compile_pages.py +++ b/benchmarks/test_benchmark_compile_pages.py @@ -1,4 +1,4 @@ -"""Benchmark tests for medium sized apps.""" +"""Benchmark tests for apps with varying page numbers.""" from __future__ import annotations @@ -126,7 +126,7 @@ class State(rx.State): def AppWithOnePage(): - """Test that background tasks work as expected.""" + """A reflex app with one page.""" from rxconfig import config # type: ignore import reflex as rx @@ -164,7 +164,7 @@ def index() -> rx.Component: def AppWithTenPages(): - """Test that background tasks work as expected.""" + """A reflex app with 10 pages.""" import reflex as rx app = rx.App(state=rx.State) @@ -199,13 +199,13 @@ def AppWithTenThousandPages(): def app_with_one_page( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 10000 pages at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: - running AppHarness instance + an AppHarness instance """ root = tmp_path_factory.mktemp(f"app1") @@ -216,13 +216,13 @@ def app_with_one_page( def app_with_ten_pages( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 10 pages at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: - running AppHarness instance + an AppHarness instance """ root = tmp_path_factory.mktemp(f"app10") yield AppHarness.create(root=root, app_source=functools.partial(AppWithTenPages, render_comp=render_multiple_pages)) # type: ignore @@ -232,13 +232,13 @@ def app_with_ten_pages( def app_with_hundred_pages( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 100 pages at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: - running AppHarness instance + an AppHarness instance """ root = tmp_path_factory.mktemp(f"app100") @@ -254,13 +254,13 @@ def app_with_hundred_pages( def app_with_thousand_pages( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 1000 pages at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture Yields: - running AppHarness instance + an AppHarness instance """ root = tmp_path_factory.mktemp(f"app1000") @@ -276,7 +276,7 @@ def app_with_thousand_pages( def app_with_ten_thousand_pages( tmp_path_factory, ) -> Generator[AppHarness, None, None]: - """Start Blank Template app at tmp_path via AppHarness. + """Create an app with 10000 pages at tmp_path via AppHarness. Args: tmp_path_factory: pytest tmp_path_factory fixture From 5715d2e53817f5b2a31c24be43bf6bb1c099a8ed Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 12 Mar 2024 10:06:20 +0000 Subject: [PATCH 58/60] address comments --- benchmarks/test_benchmark_compile_components.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/benchmarks/test_benchmark_compile_components.py b/benchmarks/test_benchmark_compile_components.py index 6b0d50cd31..fa54d1ad7b 100644 --- a/benchmarks/test_benchmark_compile_components.py +++ b/benchmarks/test_benchmark_compile_components.py @@ -123,7 +123,7 @@ def index() -> rx.Component: app.add_page(index) -def AppWithHUndredComponentOnePage(): +def AppWithHundredComponentOnePage(): """A reflex app with roughly 100 components on one page.""" import reflex as rx @@ -139,7 +139,7 @@ def AppWithThousandComponentsOnePage(): import reflex as rx def index() -> rx.Component: - return rx.center(rx.vstack(*render_component(10))) + return rx.center(rx.vstack(*render_component(1000))) app = rx.App(state=rx.State) app.add_page(index) @@ -184,7 +184,7 @@ def app_with_100_components( yield AppHarness.create( root=root, app_source=functools.partial( - AppWithHUndredComponentOnePage, render_component=render_component # type: ignore + AppWithHundredComponentOnePage, render_component=render_component # type: ignore ), ) # type: ignore From be3d2cb019409e38a012081323d3c4c655beeea1 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 12 Mar 2024 12:33:05 +0000 Subject: [PATCH 59/60] add branch name and event type --- .github/workflows/benchmarks.yml | 6 +++++- scripts/simple_app_benchmark_upload.py | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index 041940e94a..fa2226f11c 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -119,4 +119,8 @@ jobs: - name: Upload benchmark results # Only run if the database creds are available in this context. if: ${{ env.DATABASE_URL }} - run: poetry run python scripts/simple_app_benchmark_upload.py --os "${{ matrix.os }}" --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" + run: poetry run python scripts/simple_app_benchmark_upload.py --os "${{ matrix.os }}" + --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" + --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" + --db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}" + --event-type "${{ github.event_name }}" diff --git a/scripts/simple_app_benchmark_upload.py b/scripts/simple_app_benchmark_upload.py index 30e7bb98cf..0f096b735d 100644 --- a/scripts/simple_app_benchmark_upload.py +++ b/scripts/simple_app_benchmark_upload.py @@ -55,6 +55,8 @@ def insert_benchmarking_data( performance_data: list[dict], commit_sha: str, pr_title: str, + branch_name: str, + event_type: str ): """Insert the benchmarking data into the database. @@ -65,7 +67,10 @@ def insert_benchmarking_data( performance_data: The performance data of reflex web to insert. commit_sha: The commit SHA to insert. pr_title: The PR title to insert. + branch_name: The name of the branch. + event_type: Type of github event(push, pull request, etc) """ + print(f"branch name : {branch_name} | event type: {event_type}") # Serialize the JSON data simple_app_performance_json = json.dumps(performance_data) @@ -120,6 +125,16 @@ def main(): help="The PR title to insert into the database.", required=True, ) + parser.add_argument( + "--branch-name", + help="The current branch", + required=True, + ) + parser.add_argument( + "--event-type", + help="The github event type", + required=True, + ) args = parser.parse_args() # Get the results of pytest benchmarks @@ -132,6 +147,8 @@ def main(): performance_data=cleaned_benchmark_results, commit_sha=args.commit_sha, pr_title=args.pr_title, + branch_name=args.branch_name, + event_type=args.event_type ) From 361ff98ccc7d2ba5d15448598fe8eeaec8e03cf3 Mon Sep 17 00:00:00 2001 From: Elijah Date: Tue, 12 Mar 2024 13:30:32 +0000 Subject: [PATCH 60/60] add branch name actor and event type --- .github/workflows/benchmarks.yml | 2 +- scripts/simple_app_benchmark_upload.py | 20 +++++++++++++++----- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index fa2226f11c..6de6f369bd 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -123,4 +123,4 @@ jobs: --python-version "${{ matrix.python-version }}" --commit-sha "${{ github.sha }}" --benchmark-json "${{ env.OUTPUT_FILE }}" --pr-title "${{ github.event.pull_request.title }}" --db-url "${{ env.DATABASE_URL }}" --branch-name "${{ github.head_ref || github.ref_name }}" - --event-type "${{ github.event_name }}" + --event-type "${{ github.event_name }}" --actor "${{ github.actor }}" diff --git a/scripts/simple_app_benchmark_upload.py b/scripts/simple_app_benchmark_upload.py index 0f096b735d..d6c26e58a7 100644 --- a/scripts/simple_app_benchmark_upload.py +++ b/scripts/simple_app_benchmark_upload.py @@ -56,7 +56,8 @@ def insert_benchmarking_data( commit_sha: str, pr_title: str, branch_name: str, - event_type: str + event_type: str, + actor: str, ): """Insert the benchmarking data into the database. @@ -69,8 +70,8 @@ def insert_benchmarking_data( pr_title: The PR title to insert. branch_name: The name of the branch. event_type: Type of github event(push, pull request, etc) + actor: Username of the user that triggered the run. """ - print(f"branch name : {branch_name} | event type: {event_type}") # Serialize the JSON data simple_app_performance_json = json.dumps(performance_data) @@ -80,8 +81,8 @@ def insert_benchmarking_data( # Connect to the database and insert the data with psycopg2.connect(db_connection_url) as conn, conn.cursor() as cursor: insert_query = """ - INSERT INTO simple_app_benchmarks (os, python_version, commit_sha, time, pr_title, performance) - VALUES (%s, %s, %s, %s, %s, %s); + INSERT INTO simple_app_benchmarks (os, python_version, commit_sha, time, pr_title, branch_name, event_type, actor, performance) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s); """ cursor.execute( insert_query, @@ -91,6 +92,9 @@ def insert_benchmarking_data( commit_sha, current_timestamp, pr_title, + branch_name, + event_type, + actor, simple_app_performance_json, ), ) @@ -135,6 +139,11 @@ def main(): help="The github event type", required=True, ) + parser.add_argument( + "--actor", + help="Username of the user that triggered the run.", + required=True, + ) args = parser.parse_args() # Get the results of pytest benchmarks @@ -148,7 +157,8 @@ def main(): commit_sha=args.commit_sha, pr_title=args.pr_title, branch_name=args.branch_name, - event_type=args.event_type + event_type=args.event_type, + actor=args.actor, )