From f42ad0bc7dadc0341c31d3a40a3eb3aefaaff15f Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Sat, 2 Sep 2023 08:07:24 +0000 Subject: [PATCH 01/14] Generic Sphinx-Needs JS test framework --- .github/workflows/ci.yaml | 6 ++ docs/contributing.rst | 85 ++++++++++++++++++ poetry.lock | 2 +- tests/conftest.py | 90 ++++++++++++++++++- tests/doc_test/variant_doc/index.rst | 7 -- .../js_test/js-test-sn-collapse-button.cy.js | 86 ++++++++++++++++++ tests/test_js_code.py | 27 ++++++ 7 files changed, 293 insertions(+), 10 deletions(-) create mode 100644 tests/js_test/js-test-sn-collapse-button.cy.js create mode 100644 tests/test_js_code.py diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d399253fc..980a51731 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -28,6 +28,12 @@ jobs: uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} + - name: Use Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install Cypress Test Framework + run: npm install cypress - name: Install Nox Dependencies run: | python -m pip install poetry nox nox-poetry pyparsing==3.0.4 diff --git a/docs/contributing.rst b/docs/contributing.rst index 4e6777650..4143f51b6 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -101,6 +101,91 @@ Running Tests pip install -r docs/requirements.txt make test +Running JavaScript Tests in Python Test Files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +**Setup Cypress Locally** + +* Install Node JS on your computer and ensure it can be accessed through the CMD. +* Install Cypress using the npm package manager by running npm install cypress. Read more from this link https://docs.cypress.io/guides/getting-started/installing-cypress#npm-install. +* Verify if Cypress is installed correctly and is executable by running: npx cypress verify. Read more from this link https://docs.cypress.io/guides/guides/command-line. +* If everything is successful then we can use Cypress. + +**Enable Cypress Test in Python Test Files** + +* First, create a test folder to store your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also refer to the ``tests/js_test/js-test-sn-collapse-button.cy.js`` file as reference. +* In your Python test files, you must mark every JS related test case with the marker - ``jstest`` and you also need to pass the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter. For example, your test case could look like the below: + .. code-block:: python + + # test_js_code + + import pytest + + + @pytest.mark.jstest + @pytest.mark.parametrize( + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/variant_doc", + "tags": ["tag_a"], + "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js" + } + ], + indirect=True, + ) + def test_collapse_button_in_docs(test_app): + ... + + .. note:: + + The ``spec_pattern`` key is required to ensure Cypress locates your test files or folder. Visit this link for more info on how to set the `spec_pattern `_. + +* After you have set the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter, you can call the ``app.test_js()`` in your Python test case to run the JS test for the ``spec_pattern`` you provided. For example, you can use it like below: + .. code-block:: python + + # test_js_code + + import pytest + + + @pytest.mark.jstest + @pytest.mark.parametrize( + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/variant_doc", + "tags": ["tag_a"], + "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js" + } + ], + indirect=True, + ) + def test_collapse_button_in_docs(test_app): + """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" + app = test_app + app.build() + + # Call `app.test_js()` to run the JS test for a particular specPattern + js_test_result = app.test_js() + + # Check the return code and stdout + assert js_test_result["returncode"] == 0 + assert "All specs passed!" in js_test_result["stdout"].decode("utf-8") + + .. note:: + + ``app.test_js()`` will return a dictionary object containing the ``returncode``, ``stdout``, and ``stderr``. Example: + + .. code-block:: python + + return { + "returncode": 0, + "stdout": "Test passed string", + "stderr": "Errors encountered, + } + Linting & Formatting -------------------- diff --git a/poetry.lock b/poetry.lock index e2a68f25a..a1f1f885a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1993,4 +1993,4 @@ immaterial = [] [metadata] lock-version = "2.0" python-versions = ">=3.7.0,<4.0" -content-hash = "27f4d83ca2c7761c9440fc0979fe8975749d3b2ce6ff44c276fa9b2948f8deb8" +content-hash = "27f4d83ca2c7761c9440fc0979fe8975749d3b2ce6ff44c276fa9b2948f8deb8" \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 681b756e1..0a910139f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,14 @@ """Pytest conftest module containing common test configuration and fixtures.""" +import json +import os.path import shutil +import subprocess +from pathlib import Path from tempfile import mkdtemp +from typing import Any, Dict import pytest +from sphinx.application import Sphinx from sphinx.testing.path import path pytest_plugins = "sphinx.testing.fixtures" @@ -15,6 +21,82 @@ def copy_srcdir_to_tmpdir(srcdir, tmp): return tmproot +def get_abspath(relpath): + if relpath and isinstance(relpath, str): + abspath = Path(__file__).parent.joinpath(relpath).resolve() + return str(abspath) + return relpath + + +def test_js(self) -> Dict[str, Any]: + cypress_testpath = get_abspath(self.spec_pattern) + + if not cypress_testpath and not (os.path.isabs(cypress_testpath) and os.path.exists(cypress_testpath)): + return { + "returncode": 1, + "stdout": None, + "stderr": f"The spec_pattern you provided cannot be found. (spec_pattern: {self.spec_pattern})", + } + + js_test_config = { + "specPattern": cypress_testpath, + "supportFile": get_abspath("js_test/cypress/support/e2e.js"), + "fixturesFolder": False, + "baseUrl": "http://localhost:65323", + } + + cypress_config = f"{json.dumps(js_test_config)}" + cypress_config_file = get_abspath("js_test/cypress.config.js") + + # Start the HTTP server using subprocess + server_process = subprocess.Popen(["python", "-m", "http.server", "-d", f"{self.outdir}", "65323"]) + + try: + # Run the Cypress test command + completed_process = subprocess.run( + [ + "npx", + "cypress", + "run", + "--browser", + "chrome", + "--config-file", + rf"{cypress_config_file}", + "--config", + rf"{cypress_config}", + ], + capture_output=True, + ) + + # To stop the server, we can terminate the process + server_process.terminate() + server_process.wait(timeout=5) # Wait for up to 5 seconds for the process to exit + # print("Server stopped successfully.") + + # Send back return code, stdout, and stderr + return { + "returncode": completed_process.returncode, + "stdout": completed_process.stdout, + "stderr": completed_process.stderr, + } + except subprocess.TimeoutExpired: + server_process.kill() + return { + "returncode": 1, + "stdout": None, + "stderr": "Server forcibly terminated due to timeout.", + } + except (Exception, subprocess.CalledProcessError) as e: + # Stop server when an exception occurs + server_process.terminate() + server_process.wait(timeout=5) # Wait for up to 5 seconds for the process to exit + return { + "returncode": 1, + "stdout": "Server stopped due to error.", + "stderr": e, + } + + @pytest.fixture(scope="function") def test_app(make_app, request): # We create a temp-folder on our own, as the util-functions from sphinx and pytest make troubles. @@ -31,8 +113,8 @@ def test_app(make_app, request): srcdir = builder_params.get("srcdir") src_dir = copy_srcdir_to_tmpdir(srcdir, sphinx_test_tempdir) - # return sphinx.testing fixture make_app and new srcdir which in sphinx_test_tempdir - app = make_app( + # return sphinx.testing fixture make_app and new srcdir which is in sphinx_test_tempdir + app: Sphinx = make_app( buildername=builder_params.get("buildername", "html"), srcdir=src_dir, freshenv=builder_params.get("freshenv"), @@ -43,6 +125,10 @@ def test_app(make_app, request): docutilsconf=builder_params.get("docutilsconf"), parallel=builder_params.get("parallel", 0), ) + # Add the spec_pattern as an attribute to the Sphinx app object + app.spec_pattern = builder_params.get("spec_pattern", "") + # Add the test_js() function as an attribute to the Sphinx app object + app.test_js = test_js.__get__(app, Sphinx) yield app diff --git a/tests/doc_test/variant_doc/index.rst b/tests/doc_test/variant_doc/index.rst index 81983e196..ed09e7453 100644 --- a/tests/doc_test/variant_doc/index.rst +++ b/tests/doc_test/variant_doc/index.rst @@ -35,10 +35,3 @@ Variant Handling Test :maxdepth: 2 :caption: Contents: - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` diff --git a/tests/js_test/js-test-sn-collapse-button.cy.js b/tests/js_test/js-test-sn-collapse-button.cy.js new file mode 100644 index 000000000..93131f9e6 --- /dev/null +++ b/tests/js_test/js-test-sn-collapse-button.cy.js @@ -0,0 +1,86 @@ +describe('Test Sphinx Needs Collapse', () => { + it('Visit Sphinx Needs Homepage', () => { + // 1. Given a user visits http://localhost:65323/ + cy.visit('/') + + cy.get('table.need span.needs.needs_collapse').each(($el, index, $list) => { + // 2. When page loads, select all elements that matches the selector `table.need span.needs.needs_collapse` + + var id = $el.attr("id"); + var parts = id.split("__"); + var rows = parts.slice(2); + + var table = $el.closest('table'); + var need_table_id = table.closest("div[id^=SNCB-]").attr("id"); + + // 3. Check if the id of the element contains show or hide + if (parts[1] == "show") { + cy.get($el).within(() => { + // 4. Then check if `span.needs.visible` has the class `collapse_is_hidden` + cy.get('span.needs.visible').should('have.class', 'collapse_is_hidden') + }) + } else { + cy.get($el).within(() => { + // 4. Then check if `span.needs.collapse` has the class `collapse_is_hidden` + cy.get('span.needs.collapsed').should('have.class', 'collapse_is_hidden') + }) + + for (var row in rows) { + // 5. And check if `#${need_table_id} table tr.${rows[row]}` has the class `collapse_is_hidden` + cy.get(`#${need_table_id} table tr.${rows[row]}`).should('have.class', 'collapse_is_hidden') + } + } + }) + }) +}) + +describe('Test Sphinx Needs Collapse Click', () => { + it('Visit Sphinx Needs Directive page', () => { + // 1. Given a user visits http://localhost:65323/ + cy.visit('/') + + cy.get('table.need span.needs.needs_collapse').each(($el, index, $list) => { + // 2. When page loads, select all elements that matches the selector `table.need span.needs.needs_collapse` + + var id = $el.attr("id"); + var parts = id.split("__"); + var rows = parts.slice(2); + + var table = $el.closest('table'); + var need_table_id = table.closest("div[id^=SNCB-]").attr("id"); + + if (parts[1] == "show") { + // 3. Click collapse/expand button + cy.get($el).click() + + for (var row in rows) { + // 4. And check if `#${need_table_id} table tr.${rows[row]}` has the class `collapse_is_hidden` + cy.get(`#${need_table_id} table tr.${rows[row]}`).should('have.class', 'collapse_is_hidden') + } + + cy.get($el).within(() => { + // 5. Then check if `span.needs.collapse` has the class `collapse_is_hidden` + cy.get('span.needs.collapsed').should('have.class', 'collapse_is_hidden') + // 6. And check if `span.needs.visible` has the class `collapse_is_hidden` + cy.get('span.needs.visible').should('not.have.class', 'collapse_is_hidden') + }) + } else{ + // 3. Click collapse/expand button + cy.get($el).click() + + for (var row in rows) { + // 4. And check if `#${need_table_id} table tr.${rows[row]}` has the class `collapse_is_hidden` + cy.get(`#${need_table_id} table tr.${rows[row]}`).should('not.have.class', 'collapse_is_hidden') + } + + cy.get($el).within(() => { + // 5. Then check if `span.needs.collapse` has the class `collapse_is_hidden` + cy.get('span.needs.collapsed').should('not.have.class', 'collapse_is_hidden') + // 6. Check if `span.needs.visible` has the class `collapse_is_hidden` + cy.get('span.needs.visible').should('have.class', 'collapse_is_hidden') + }) + } + + }) + }) +}) \ No newline at end of file diff --git a/tests/test_js_code.py b/tests/test_js_code.py new file mode 100644 index 000000000..a80648f9f --- /dev/null +++ b/tests/test_js_code.py @@ -0,0 +1,27 @@ +import pytest + + +@pytest.mark.jstest +@pytest.mark.parametrize( + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/variant_doc", + "tags": ["tag_a"], + "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js", + } + ], + indirect=True, +) +def test_collapse_button_in_docs(test_app): + """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" + app = test_app + app.build() + + # Call `app.test_js()` to run the JS test for a particular specPattern + js_test_result = app.test_js() + + # Check the return code and stdout + assert js_test_result["returncode"] == 0 + assert "All specs passed!" in js_test_result["stdout"].decode("utf-8") From 3ef02551d3edac7131c270d8041aadf5a6ac3415 Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Thu, 7 Sep 2023 15:55:37 +0000 Subject: [PATCH 02/14] Registered custom `jstest` marker in pyproject.toml --- docs/contributing.rst | 2 +- pyproject.toml | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index 4143f51b6..a084c7bf9 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -112,7 +112,7 @@ Running JavaScript Tests in Python Test Files **Enable Cypress Test in Python Test Files** -* First, create a test folder to store your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also refer to the ``tests/js_test/js-test-sn-collapse-button.cy.js`` file as reference. +* First, create a test folder to store your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also check the ``tests/js_test/js-test-sn-collapse-button.cy.js`` file as reference. * In your Python test files, you must mark every JS related test case with the marker - ``jstest`` and you also need to pass the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter. For example, your test case could look like the below: .. code-block:: python diff --git a/pyproject.toml b/pyproject.toml index b931398e7..9c799497b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -73,6 +73,9 @@ packaging = "*" [tool.pytest.ini_options] asyncio_mode= "auto" +markers = [ + "jstest: marks tests as JavaScript test (deselect with '-m \"not jstest\"')", +] [tool.black] line-length = 120 From 634a608ac21d186bee7fd00eeaf4658e39ab1c69 Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Thu, 7 Sep 2023 16:11:55 +0000 Subject: [PATCH 03/14] Added another JS test case and updated Makefile. --- Makefile | 4 ++-- tests/test_js_code.py | 27 ++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 554dddd4c..3f3c8b322 100644 --- a/Makefile +++ b/Makefile @@ -10,11 +10,11 @@ lint: .PHONY: test test: - poetry run pytest -n auto --tb=long --ignore=tests/benchmarks tests/ + poetry run pytest -n auto -m "not jstest" --tb=long --ignore=tests/benchmarks tests/ .PHONY: test test-short: - poetry run pytest -n auto --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ .PHONY: benchmark-time benchmark-time: diff --git a/tests/test_js_code.py b/tests/test_js_code.py index a80648f9f..e34892ba8 100644 --- a/tests/test_js_code.py +++ b/tests/test_js_code.py @@ -14,7 +14,32 @@ ], indirect=True, ) -def test_collapse_button_in_docs(test_app): +def test_collapse_button_in_variant_doc(test_app): + """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" + app = test_app + app.build() + + # Call `app.test_js()` to run the JS test for a particular specPattern + js_test_result = app.test_js() + + # Check the return code and stdout + assert js_test_result["returncode"] == 0 + assert "All specs passed!" in js_test_result["stdout"].decode("utf-8") + + +@pytest.mark.jstest +@pytest.mark.parametrize( + "test_app", + [ + { + "buildername": "html", + "srcdir": "doc_test/doc_basic", + "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js", + } + ], + indirect=True, +) +def test_collapse_button_in_doc_basic(test_app): """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" app = test_app app.build() From 31edb48d5f169c998b08ec51d30bcbfdbcc35bf9 Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Sat, 23 Sep 2023 04:57:34 +0000 Subject: [PATCH 04/14] WIP: Restructured the JS testing framework --- Makefile | 2 +- docs/contributing.rst | 10 +- poetry.lock | 169 +----------------- tests/conftest.py | 141 ++++++++++----- ...-button.cy.js => sn-collapse-button.cy.js} | 0 tests/test_needs_external_needs_build.py | 16 +- ..._js_code.py => test_sn_collapse_button.py} | 8 +- 7 files changed, 122 insertions(+), 224 deletions(-) rename tests/js_test/{js-test-sn-collapse-button.cy.js => sn-collapse-button.cy.js} (100%) rename tests/{test_js_code.py => test_sn_collapse_button.py} (83%) diff --git a/Makefile b/Makefile index 3f3c8b322..5678f02b8 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ test: .PHONY: test test-short: - poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + poetry run pytest -n auto -m "jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ .PHONY: benchmark-time benchmark-time: diff --git a/docs/contributing.rst b/docs/contributing.rst index a084c7bf9..1d0f87d29 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -106,17 +106,17 @@ Running JavaScript Tests in Python Test Files **Setup Cypress Locally** * Install Node JS on your computer and ensure it can be accessed through the CMD. -* Install Cypress using the npm package manager by running npm install cypress. Read more from this link https://docs.cypress.io/guides/getting-started/installing-cypress#npm-install. -* Verify if Cypress is installed correctly and is executable by running: npx cypress verify. Read more from this link https://docs.cypress.io/guides/guides/command-line. +* Install Cypress using the npm package manager by running ``npm install cypress``. Visit this link for more information on `how to install Cypress `_. +* Verify if Cypress is installed correctly and is executable by running: ``npx cypress verify``. Get out this page for more information about `Cypress commandline `_. * If everything is successful then we can use Cypress. **Enable Cypress Test in Python Test Files** -* First, create a test folder to store your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also check the ``tests/js_test/js-test-sn-collapse-button.cy.js`` file as reference. +* Under the ``js_test`` folder, you can save your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also check the ``tests/js_test/sn-collapse-button.cy.js`` file as reference. * In your Python test files, you must mark every JS related test case with the marker - ``jstest`` and you also need to pass the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter. For example, your test case could look like the below: .. code-block:: python - # test_js_code + # tests/test_sn_collapse_button.py import pytest @@ -144,7 +144,7 @@ Running JavaScript Tests in Python Test Files * After you have set the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter, you can call the ``app.test_js()`` in your Python test case to run the JS test for the ``spec_pattern`` you provided. For example, you can use it like below: .. code-block:: python - # test_js_code + # tests/test_sn_collapse_button.py import pytest diff --git a/poetry.lock b/poetry.lock index a1f1f885a..53cee8be3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,9 @@ -# This file is automatically @generated by Poetry and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "alabaster" version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -16,7 +15,6 @@ files = [ name = "attrs" version = "22.2.0" description = "Classes Without Boilerplate" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -35,7 +33,6 @@ tests-no-zope = ["cloudpickle", "cloudpickle", "hypothesis", "hypothesis", "mypy name = "babel" version = "2.11.0" description = "Internationalization utilities" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -50,7 +47,6 @@ pytz = ">=2015.7" name = "black" version = "22.12.0" description = "The uncompromising code formatter." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -87,7 +83,6 @@ uvloop = ["uvloop (>=0.15.2)"] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -99,7 +94,6 @@ files = [ name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." -category = "dev" optional = false python-versions = ">=3.6.1" files = [ @@ -111,7 +105,6 @@ files = [ name = "charset-normalizer" version = "3.0.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "main" optional = false python-versions = "*" files = [ @@ -209,7 +202,6 @@ files = [ name = "click" version = "8.1.3" description = "Composable command line interface toolkit" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -225,7 +217,6 @@ importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ @@ -237,7 +228,6 @@ files = [ name = "cycler" version = "0.11.0" description = "Composable style cycles" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -249,7 +239,6 @@ files = [ name = "distlib" version = "0.3.6" description = "Distribution utilities" -category = "dev" optional = false python-versions = "*" files = [ @@ -261,7 +250,6 @@ files = [ name = "docutils" version = "0.19" description = "Docutils -- Python Documentation Utilities" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -273,7 +261,6 @@ files = [ name = "docutils-stubs" version = "0.0.22" description = "PEP 561 type stubs for docutils" -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -288,7 +275,6 @@ docutils = ">=0.14" name = "exceptiongroup" version = "1.1.0" description = "Backport of PEP 654 (exception groups)" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -303,7 +289,6 @@ test = ["pytest (>=6)"] name = "execnet" version = "1.9.0" description = "execnet: rapid multi-Python deployment" -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ @@ -318,7 +303,6 @@ testing = ["pre-commit"] name = "filelock" version = "3.9.0" description = "A platform independent file lock." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -334,7 +318,6 @@ testing = ["covdefaults (>=2.2.2)", "coverage (>=7.0.1)", "pytest (>=7.2)", "pyt name = "fonttools" version = "4.38.0" description = "Tools to manipulate font files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -360,7 +343,6 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] name = "identify" version = "2.5.18" description = "File identification library for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -375,7 +357,6 @@ license = ["ukkonen"] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -387,7 +368,6 @@ files = [ name = "imagesize" version = "1.4.1" description = "Getting image size from png/jpeg/jpeg2000/gif file" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ @@ -399,7 +379,6 @@ files = [ name = "importlib-metadata" version = "6.0.0" description = "Read metadata from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -420,7 +399,6 @@ testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packag name = "importlib-resources" version = "5.10.2" description = "Read resources from Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -439,7 +417,6 @@ testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-chec name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -451,7 +428,6 @@ files = [ name = "isort" version = "5.11.5" description = "A Python utility / library to sort Python imports." -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -469,7 +445,6 @@ requirements-deprecated-finder = ["pip-api", "pipreqs"] name = "jinja2" version = "3.1.2" description = "A very fast and expressive template engine." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -487,7 +462,6 @@ i18n = ["Babel (>=2.7)"] name = "jsonschema" version = "4.17.3" description = "An implementation of JSON Schema validation for Python" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -511,7 +485,6 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- name = "kiwisolver" version = "1.4.4" description = "A fast implementation of the Cassowary constraint solver" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -592,7 +565,6 @@ typing-extensions = {version = "*", markers = "python_version < \"3.8\""} name = "lxml" version = "4.9.2" description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." -category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" files = [ @@ -685,7 +657,6 @@ source = ["Cython (>=0.29.7)"] name = "markdown-it-py" version = "2.2.0" description = "Python port of markdown-it. Markdown parsing, done right!" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -711,7 +682,6 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] name = "markupsafe" version = "2.1.2" description = "Safely add untrusted strings to HTML/XML markup." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -771,7 +741,6 @@ files = [ name = "matplotlib" version = "3.5.3" description = "Python plotting package" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -826,7 +795,6 @@ python-dateutil = ">=2.7" name = "mdurl" version = "0.1.2" description = "Markdown URL utilities" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -838,7 +806,6 @@ files = [ name = "memray" version = "1.6.0" description = "A memory profiler for Python applications" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -879,7 +846,6 @@ test = ["Cython", "greenlet", "ipython", "pytest", "pytest-cov"] name = "mypy" version = "0.991" description = "Optional static typing for Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -931,7 +897,6 @@ reports = ["lxml"] name = "mypy-extensions" version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." -category = "dev" optional = false python-versions = ">=3.5" files = [ @@ -943,7 +908,6 @@ files = [ name = "nodeenv" version = "1.7.0" description = "Node.js virtual environment builder" -category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ @@ -958,7 +922,6 @@ setuptools = "*" name = "numpy" version = "1.21.1" description = "NumPy is the fundamental package for array computing with Python." -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -996,7 +959,6 @@ files = [ name = "packaging" version = "23.0" description = "Core utilities for Python packages" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1008,7 +970,6 @@ files = [ name = "pathspec" version = "0.11.0" description = "Utility library for gitignore style pattern matching of file paths." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1020,7 +981,6 @@ files = [ name = "pillow" version = "9.4.0" description = "Python Imaging Library (Fork)" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1111,7 +1071,6 @@ tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "pa name = "pkgutil-resolve-name" version = "1.3.10" description = "Resolve a name to an object." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1123,7 +1082,6 @@ files = [ name = "platformdirs" version = "3.0.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1142,7 +1100,6 @@ test = ["appdirs (==1.4.4)", "covdefaults (>=2.2.2)", "pytest (>=7.2.1)", "pytes name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1161,7 +1118,6 @@ testing = ["pytest", "pytest-benchmark"] name = "pre-commit" version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1181,7 +1137,6 @@ virtualenv = ">=20.10.0" name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" -category = "dev" optional = false python-versions = "*" files = [ @@ -1189,64 +1144,10 @@ files = [ {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] -[[package]] -name = "pydantic" -version = "1.10.5" -description = "Data validation and settings management using python type hints" -category = "dev" -optional = true -python-versions = ">=3.7" -files = [ - {file = "pydantic-1.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5920824fe1e21cbb3e38cf0f3dd24857c8959801d1031ce1fac1d50857a03bfb"}, - {file = "pydantic-1.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3bb99cf9655b377db1a9e47fa4479e3330ea96f4123c6c8200e482704bf1eda2"}, - {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2185a3b3d98ab4506a3f6707569802d2d92c3a7ba3a9a35683a7709ea6c2aaa2"}, - {file = "pydantic-1.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f582cac9d11c227c652d3ce8ee223d94eb06f4228b52a8adaafa9fa62e73d5c9"}, - {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c9e5b778b6842f135902e2d82624008c6a79710207e28e86966cd136c621bfee"}, - {file = "pydantic-1.10.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:72ef3783be8cbdef6bca034606a5de3862be6b72415dc5cb1fb8ddbac110049a"}, - {file = "pydantic-1.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:45edea10b75d3da43cfda12f3792833a3fa70b6eee4db1ed6aed528cef17c74e"}, - {file = "pydantic-1.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:63200cd8af1af2c07964546b7bc8f217e8bda9d0a2ef0ee0c797b36353914984"}, - {file = "pydantic-1.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:305d0376c516b0dfa1dbefeae8c21042b57b496892d721905a6ec6b79494a66d"}, - {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1fd326aff5d6c36f05735c7c9b3d5b0e933b4ca52ad0b6e4b38038d82703d35b"}, - {file = "pydantic-1.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bb0452d7b8516178c969d305d9630a3c9b8cf16fcf4713261c9ebd465af0d73"}, - {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9a9d9155e2a9f38b2eb9374c88f02fd4d6851ae17b65ee786a87d032f87008f8"}, - {file = "pydantic-1.10.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f836444b4c5ece128b23ec36a446c9ab7f9b0f7981d0d27e13a7c366ee163f8a"}, - {file = "pydantic-1.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:8481dca324e1c7b715ce091a698b181054d22072e848b6fc7895cd86f79b4449"}, - {file = "pydantic-1.10.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:87f831e81ea0589cd18257f84386bf30154c5f4bed373b7b75e5cb0b5d53ea87"}, - {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ce1612e98c6326f10888df951a26ec1a577d8df49ddcaea87773bfbe23ba5cc"}, - {file = "pydantic-1.10.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58e41dd1e977531ac6073b11baac8c013f3cd8706a01d3dc74e86955be8b2c0c"}, - {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:6a4b0aab29061262065bbdede617ef99cc5914d1bf0ddc8bcd8e3d7928d85bd6"}, - {file = "pydantic-1.10.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:36e44a4de37b8aecffa81c081dbfe42c4d2bf9f6dff34d03dce157ec65eb0f15"}, - {file = "pydantic-1.10.5-cp37-cp37m-win_amd64.whl", hash = "sha256:261f357f0aecda005934e413dfd7aa4077004a174dafe414a8325e6098a8e419"}, - {file = "pydantic-1.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b429f7c457aebb7fbe7cd69c418d1cd7c6fdc4d3c8697f45af78b8d5a7955760"}, - {file = "pydantic-1.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:663d2dd78596c5fa3eb996bc3f34b8c2a592648ad10008f98d1348be7ae212fb"}, - {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51782fd81f09edcf265823c3bf43ff36d00db246eca39ee765ef58dc8421a642"}, - {file = "pydantic-1.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c428c0f64a86661fb4873495c4fac430ec7a7cef2b8c1c28f3d1a7277f9ea5ab"}, - {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:76c930ad0746c70f0368c4596020b736ab65b473c1f9b3872310a835d852eb19"}, - {file = "pydantic-1.10.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3257bd714de9db2102b742570a56bf7978e90441193acac109b1f500290f5718"}, - {file = "pydantic-1.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:f5bee6c523d13944a1fdc6f0525bc86dbbd94372f17b83fa6331aabacc8fd08e"}, - {file = "pydantic-1.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:532e97c35719f137ee5405bd3eeddc5c06eb91a032bc755a44e34a712420daf3"}, - {file = "pydantic-1.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ca9075ab3de9e48b75fa8ccb897c34ccc1519177ad8841d99f7fd74cf43be5bf"}, - {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd46a0e6296346c477e59a954da57beaf9c538da37b9df482e50f836e4a7d4bb"}, - {file = "pydantic-1.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3353072625ea2a9a6c81ad01b91e5c07fa70deb06368c71307529abf70d23325"}, - {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3f9d9b2be177c3cb6027cd67fbf323586417868c06c3c85d0d101703136e6b31"}, - {file = "pydantic-1.10.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b473d00ccd5c2061fd896ac127b7755baad233f8d996ea288af14ae09f8e0d1e"}, - {file = "pydantic-1.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:5f3bc8f103b56a8c88021d481410874b1f13edf6e838da607dcb57ecff9b4594"}, - {file = "pydantic-1.10.5-py3-none-any.whl", hash = "sha256:7c5b94d598c90f2f46b3a983ffb46ab806a67099d118ae0da7ef21a2a4033b28"}, - {file = "pydantic-1.10.5.tar.gz", hash = "sha256:9e337ac83686645a46db0e825acceea8e02fca4062483f40e9ae178e8bd1103a"}, -] - -[package.dependencies] -typing-extensions = ">=4.2.0" - -[package.extras] -dotenv = ["python-dotenv (>=0.10.4)"] -email = ["email-validator (>=1.0.3)"] - [[package]] name = "pygments" version = "2.14.0" description = "Pygments is a syntax highlighting package written in Python." -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1261,7 +1162,6 @@ plugins = ["importlib-metadata"] name = "pyparsing" version = "3.0.9" description = "pyparsing module - Classes and methods to define and execute parsing grammars" -category = "main" optional = false python-versions = ">=3.6.8" files = [ @@ -1276,7 +1176,6 @@ diagrams = ["jinja2", "railroad-diagrams"] name = "pyrsistent" version = "0.19.3" description = "Persistent/Functional/Immutable data structures" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1313,7 +1212,6 @@ files = [ name = "pytest" version = "7.2.1" description = "pytest: simple powerful testing with Python" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1338,7 +1236,6 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2. name = "pytest-benchmark" version = "4.0.0" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1359,7 +1256,6 @@ histogram = ["pygal", "pygaljs"] name = "pytest-xdist" version = "3.2.0" description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1380,7 +1276,6 @@ testing = ["filelock"] name = "python-dateutil" version = "2.8.2" description = "Extensions to the standard Python datetime module" -category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" files = [ @@ -1395,7 +1290,6 @@ six = ">=1.5" name = "pytz" version = "2022.7.1" description = "World timezone definitions, modern and historical" -category = "main" optional = false python-versions = "*" files = [ @@ -1407,7 +1301,6 @@ files = [ name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1457,7 +1350,6 @@ files = [ name = "requests" version = "2.28.2" description = "Python HTTP for Humans." -category = "main" optional = false python-versions = ">=3.7, <4" files = [ @@ -1479,7 +1371,6 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] name = "requests-file" version = "1.5.1" description = "File transport adapter for Requests" -category = "main" optional = false python-versions = "*" files = [ @@ -1495,7 +1386,6 @@ six = "*" name = "requests-mock" version = "1.10.0" description = "Mock out responses from the requests package" -category = "dev" optional = false python-versions = "*" files = [ @@ -1515,7 +1405,6 @@ test = ["fixtures", "mock", "purl", "pytest", "requests-futures", "sphinx", "tes name = "responses" version = "0.22.0" description = "A utility library for mocking out the `requests` Python library." -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1537,7 +1426,6 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy name = "rich" version = "13.3.1" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -category = "dev" optional = false python-versions = ">=3.7.0" files = [ @@ -1557,7 +1445,6 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] name = "setuptools" version = "67.3.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1574,7 +1461,6 @@ testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs ( name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1586,7 +1472,6 @@ files = [ name = "snowballstemmer" version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." -category = "main" optional = false python-versions = "*" files = [ @@ -1598,7 +1483,6 @@ files = [ name = "sphinx" version = "5.3.0" description = "Python documentation generator" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1634,7 +1518,6 @@ test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] name = "sphinx-data-viewer" version = "0.1.2" description = "Sphinx extension to show dta in an interacitve list view" -category = "main" optional = false python-versions = ">=3.6,<4.0" files = [ @@ -1644,35 +1527,10 @@ files = [ [package.extras] docs = ["sphinx (>=4,<5)"] -[[package]] -name = "sphinx-immaterial" -version = "0.7.3" -description = "Adaptation of mkdocs-material theme for the Sphinx documentation system" -category = "dev" -optional = true -python-versions = ">=3.7" -files = [ - {file = "sphinx_immaterial-0.7.3-py3-none-any.whl", hash = "sha256:674440fc4eb39aaa4ec9c098fd09b7cd696754438e93ddd539d19828bab0f509"}, - {file = "sphinx_immaterial-0.7.3.tar.gz", hash = "sha256:098e1069494fea1482601c33d6a124eff023d19b1616fdb6c46b482177f3e7b7"}, -] - -[package.dependencies] -markupsafe = "*" -pydantic = "*" -sphinx = ">=4.0" -typing-extensions = "*" - -[package.extras] -clang-format = ["clang-format"] -json = ["pyyaml"] -jsonschema-validation = ["jsonschema"] -keys = ["pymdown-extensions"] - [[package]] name = "sphinxcontrib-applehelp" version = "1.0.2" description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1688,7 +1546,6 @@ test = ["pytest"] name = "sphinxcontrib-devhelp" version = "1.0.2" description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1704,7 +1561,6 @@ test = ["pytest"] name = "sphinxcontrib-htmlhelp" version = "2.0.0" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" -category = "main" optional = false python-versions = ">=3.6" files = [ @@ -1720,7 +1576,6 @@ test = ["html5lib", "pytest"] name = "sphinxcontrib-jsmath" version = "1.0.1" description = "A sphinx extension which renders display math in HTML via JavaScript" -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1735,7 +1590,6 @@ test = ["flake8", "mypy", "pytest"] name = "sphinxcontrib-plantuml" version = "0.24.1" description = "Sphinx \"plantuml\" extension" -category = "dev" optional = false python-versions = "*" files = [ @@ -1752,7 +1606,6 @@ test = ["Pillow", "flake8", "pytest"] name = "sphinxcontrib-qthelp" version = "1.0.3" description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1768,7 +1621,6 @@ test = ["pytest"] name = "sphinxcontrib-serializinghtml" version = "1.1.5" description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)." -category = "main" optional = false python-versions = ">=3.5" files = [ @@ -1784,7 +1636,6 @@ test = ["pytest"] name = "tabulate" version = "0.9.0" description = "Pretty-print tabular data" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1799,7 +1650,6 @@ widechars = ["wcwidth"] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ @@ -1811,7 +1661,6 @@ files = [ name = "tomli" version = "2.0.1" description = "A lil' TOML parser" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1823,7 +1672,6 @@ files = [ name = "typed-ast" version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" -category = "dev" optional = false python-versions = ">=3.6" files = [ @@ -1857,7 +1705,6 @@ files = [ name = "types-docutils" version = "0.19.1.4" description = "Typing stubs for docutils" -category = "dev" optional = false python-versions = "*" files = [ @@ -1869,7 +1716,6 @@ files = [ name = "types-requests" version = "2.28.11.13" description = "Typing stubs for requests" -category = "dev" optional = false python-versions = "*" files = [ @@ -1884,7 +1730,6 @@ types-urllib3 = "<1.27" name = "types-setuptools" version = "65.7.0.4" description = "Typing stubs for setuptools" -category = "dev" optional = false python-versions = "*" files = [ @@ -1899,7 +1744,6 @@ types-docutils = "*" name = "types-toml" version = "0.10.8.4" description = "Typing stubs for toml" -category = "dev" optional = false python-versions = "*" files = [ @@ -1911,7 +1755,6 @@ files = [ name = "types-urllib3" version = "1.26.25.6" description = "Typing stubs for urllib3" -category = "dev" optional = false python-versions = "*" files = [ @@ -1923,7 +1766,6 @@ files = [ name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1935,7 +1777,6 @@ files = [ name = "urllib3" version = "1.26.14" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ @@ -1952,7 +1793,6 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "virtualenv" version = "20.19.0" description = "Virtual Python Environment builder" -category = "dev" optional = false python-versions = ">=3.7" files = [ @@ -1974,7 +1814,6 @@ test = ["covdefaults (>=2.2.2)", "coverage (>=7.1)", "coverage-enable-subprocess name = "zipp" version = "3.13.0" description = "Backport of pathlib-compatible object wrapper for zip files" -category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1986,11 +1825,7 @@ files = [ docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] -[extras] -docs = ["sphinx"] -immaterial = [] - [metadata] lock-version = "2.0" python-versions = ">=3.7.0,<4.0" -content-hash = "27f4d83ca2c7761c9440fc0979fe8975749d3b2ce6ff44c276fa9b2948f8deb8" \ No newline at end of file +content-hash = "a81eff925786559b7e3198159981309b10bd0035fb9e9dab27e81017fdfa36d6" diff --git a/tests/conftest.py b/tests/conftest.py index 0a910139f..83c23988e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,57 +1,108 @@ """Pytest conftest module containing common test configuration and fixtures.""" import json import os.path +import secrets import shutil -import subprocess +import string +import tempfile from pathlib import Path -from tempfile import mkdtemp from typing import Any, Dict import pytest from sphinx.application import Sphinx from sphinx.testing.path import path +from xprocess import ProcessStarter pytest_plugins = "sphinx.testing.fixtures" -def copy_srcdir_to_tmpdir(srcdir, tmp): +def generate_random_string() -> str: + characters = string.ascii_letters + string.digits + return "".join(secrets.choice(characters) for i in range(10)) + + +def copy_srcdir_to_tmpdir(srcdir: path, tmp: path) -> path: srcdir = path(__file__).parent.abspath() / srcdir - tmproot = tmp / path(srcdir).basename() + tmproot = tmp.joinpath(generate_random_string()) / path(srcdir).basename() shutil.copytree(srcdir, tmproot) return tmproot -def get_abspath(relpath): - if relpath and isinstance(relpath, str): +def get_abspath(relpath: str) -> str: + """ + Get the absolute path from a relative path. + + This function returns an absolute path relative to the conftest.py file. + + :param relpath: The relative path to convert. + :return: The absolute path, or the input if it's not a valid relative path. + """ + if isinstance(relpath, str) and relpath: abspath = Path(__file__).parent.joinpath(relpath).resolve() return str(abspath) return relpath +@pytest.fixture(scope="session") +def test_server(xprocess, sphinx_test_tempdir): + class Starter(ProcessStarter): + # pattern = "HTTP Server started on port [0-9]+!" + pattern = "Serving HTTP on [0-9.]+ port [0-9]+" + # wait for 10 seconds before timing out + timeout = 10 + # terminate and clean up all started processes and their resources upon interruptions + # during pytest runs (CTRL+C, SIGINT and internal errors) + terminate_on_interrupt = True + # command to start process + # args = ["python", f"{path(__file__).parent.abspath()}/server.py", sphinx_test_tempdir] + args = ["python", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", "127.0.0.1", "62343"] + + # ensure process is running and return its logfile + logfile = xprocess.ensure("http_server", Starter, restart=True) # noqa:F841 + http_server_process = xprocess.getinfo("http_server") + server_url = "http://localhost:62343" + http_server_process.url = server_url + + yield http_server_process + + # clean up whole process tree afterward + http_server_process.terminate() + + def test_js(self) -> Dict[str, Any]: + """ + Executes Cypress tests using the specified `spec_pattern`. + + :param self: An instance of the :class:`Sphinx` application object this function is bounded to. + :return: A dictionary with test execution information. + Keys: + - 'returncode': Return code of the Cypress test execution. + - 'stdout': Standard output of the Cypress test execution. + - 'stderr': Standard error of the Cypress test execution. + """ cypress_testpath = get_abspath(self.spec_pattern) - if not cypress_testpath and not (os.path.isabs(cypress_testpath) and os.path.exists(cypress_testpath)): + if not cypress_testpath or not (os.path.isabs(cypress_testpath) and os.path.exists(cypress_testpath)): return { "returncode": 1, "stdout": None, - "stderr": f"The spec_pattern you provided cannot be found. (spec_pattern: {self.spec_pattern})", + "stderr": f"The spec_pattern '{self.spec_pattern}' cannot be found.", } - + _, out_dir = str(self.outdir).split("sn_test_build_data") + srcdir_url = f"http://localhost:62343/{out_dir.lstrip('/')}/" js_test_config = { "specPattern": cypress_testpath, "supportFile": get_abspath("js_test/cypress/support/e2e.js"), "fixturesFolder": False, - "baseUrl": "http://localhost:65323", + "baseUrl": srcdir_url, } - cypress_config = f"{json.dumps(js_test_config)}" + cypress_config = json.dumps(js_test_config) cypress_config_file = get_abspath("js_test/cypress.config.js") - # Start the HTTP server using subprocess - server_process = subprocess.Popen(["python", "-m", "http.server", "-d", f"{self.outdir}", "65323"]) - try: + import subprocess + # Run the Cypress test command completed_process = subprocess.run( [ @@ -68,46 +119,49 @@ def test_js(self) -> Dict[str, Any]: capture_output=True, ) - # To stop the server, we can terminate the process - server_process.terminate() - server_process.wait(timeout=5) # Wait for up to 5 seconds for the process to exit - # print("Server stopped successfully.") - # Send back return code, stdout, and stderr return { "returncode": completed_process.returncode, "stdout": completed_process.stdout, "stderr": completed_process.stderr, } - except subprocess.TimeoutExpired: - server_process.kill() - return { - "returncode": 1, - "stdout": None, - "stderr": "Server forcibly terminated due to timeout.", - } except (Exception, subprocess.CalledProcessError) as e: - # Stop server when an exception occurs - server_process.terminate() - server_process.wait(timeout=5) # Wait for up to 5 seconds for the process to exit return { "returncode": 1, - "stdout": "Server stopped due to error.", + "stdout": "", "stderr": e, } -@pytest.fixture(scope="function") -def test_app(make_app, request): +@pytest.fixture(scope="session") +def sphinx_test_tempdir(): # We create a temp-folder on our own, as the util-functions from sphinx and pytest make troubles. # It seems like they reuse certain-temp names - sphinx_test_tempdir = path(mkdtemp()) + sphinx_test_tempdir = path(tempfile.gettempdir()).joinpath("sn_test_build_data") + utils_dir = sphinx_test_tempdir.joinpath("utils") - builder_params = request.param + if not (sphinx_test_tempdir.exists() and sphinx_test_tempdir.isdir()): + sphinx_test_tempdir.makedirs() + if not (utils_dir.exists() and utils_dir.isdir()): + utils_dir.makedirs() - # copy plantuml.jar to current test temdir + # copy plantuml.jar to current test tempdir. We want to do this once + # since the same plantuml.jar is used for each test plantuml_jar_file = path(__file__).parent.abspath() / "doc_test/utils" - shutil.copytree(plantuml_jar_file, sphinx_test_tempdir / "utils") + shutil.copytree(plantuml_jar_file, utils_dir, dirs_exist_ok=True) + + return sphinx_test_tempdir + + +@pytest.fixture(scope="function") +def test_app(make_app, sphinx_test_tempdir, request): + builder_params = request.param + + sphinx_conf_overrides = builder_params.get("confoverrides", {}) + # Since we don't want copy the plantuml.jar file for each test function, + # we need to override the plantuml conf variable and set it to what we have already + plantuml = "java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") + sphinx_conf_overrides.update(plantuml=plantuml) # copy test srcdir to test temporary directory sphinx_test_tempdir srcdir = builder_params.get("srcdir") @@ -118,7 +172,7 @@ def test_app(make_app, request): buildername=builder_params.get("buildername", "html"), srcdir=src_dir, freshenv=builder_params.get("freshenv"), - confoverrides=builder_params.get("confoverrides"), + confoverrides=sphinx_conf_overrides, status=builder_params.get("status"), warning=builder_params.get("warning"), tags=builder_params.get("tags"), @@ -126,11 +180,16 @@ def test_app(make_app, request): parallel=builder_params.get("parallel", 0), ) # Add the spec_pattern as an attribute to the Sphinx app object - app.spec_pattern = builder_params.get("spec_pattern", "") - # Add the test_js() function as an attribute to the Sphinx app object + app.spec_pattern = builder_params.get("spec_pattern", "*.cy.js") + # Add the ``test_js`` function as an attribute to the Sphinx app object + # This is done by accessing the special method ``__get__`` which allows the ``test_js`` function + # to be bound to the Sphinx app object, enabling it to access the object's attributes. + # We can later call ``test_js`` function as an attribute of the Sphinx app object. + # Since we've bound the ``test_js`` function to the Sphinx object using ``__get__``, + # ``test_js`` behaves like a method. app.test_js = test_js.__get__(app, Sphinx) yield app - # cleanup test temporary directory - shutil.rmtree(sphinx_test_tempdir, False) + # Clean up the srcdir of each Sphinx app after the test function has executed + shutil.rmtree(src_dir.parent.abspath(), ignore_errors=True) diff --git a/tests/js_test/js-test-sn-collapse-button.cy.js b/tests/js_test/sn-collapse-button.cy.js similarity index 100% rename from tests/js_test/js-test-sn-collapse-button.cy.js rename to tests/js_test/sn-collapse-button.cy.js diff --git a/tests/test_needs_external_needs_build.py b/tests/test_needs_external_needs_build.py index 4dfc21878..0a3c8876b 100644 --- a/tests/test_needs_external_needs_build.py +++ b/tests/test_needs_external_needs_build.py @@ -1,3 +1,4 @@ +import os import sys from pathlib import Path @@ -10,26 +11,29 @@ @pytest.mark.parametrize( "test_app", [{"buildername": "html", "srcdir": "doc_test/doc_needs_external_needs"}], indirect=True ) -def test_doc_build_html(test_app): +def test_doc_build_html(test_app, sphinx_test_tempdir): import subprocess app = test_app src_dir = Path(app.srcdir) out_dir = Path(app.outdir) - output = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) - assert not output.stderr, f"Should not have stderr: {output.stderr}" + plantuml = r"java -Djava.awt.headless=true -jar %s" % os.path.join(sphinx_test_tempdir, "utils", "plantuml.jar") + output = subprocess.run( + ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True + ) + assert not output.stderr, f"Build failed with stderr: {output.stderr}" # run second time and check - output_second = subprocess.run(["sphinx-build", "-M", "html", src_dir, out_dir], capture_output=True) + output_second = subprocess.run( + ["sphinx-build", "-b", "html", "-D", rf"plantuml={plantuml}", src_dir, out_dir], capture_output=True + ) assert not output_second.stderr # check if incremental build used # first build output - assert "making output directory" in output.stdout.decode("utf-8") assert "updating environment: [new config] 3 added, 0 changed, 0 removed" in output.stdout.decode("utf-8") # second build output - assert "making output directory" not in output_second.stdout.decode("utf-8") assert "loading pickled environment" in output_second.stdout.decode("utf-8") assert "updating environment: [new config] 3 added, 0 changed, 0 removed" not in output_second.stdout.decode( "utf-8" diff --git a/tests/test_js_code.py b/tests/test_sn_collapse_button.py similarity index 83% rename from tests/test_js_code.py rename to tests/test_sn_collapse_button.py index e34892ba8..1aa37a23f 100644 --- a/tests/test_js_code.py +++ b/tests/test_sn_collapse_button.py @@ -9,12 +9,12 @@ "buildername": "html", "srcdir": "doc_test/variant_doc", "tags": ["tag_a"], - "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js", + "spec_pattern": "js_test/sn-collapse-button.cy.js", } ], indirect=True, ) -def test_collapse_button_in_variant_doc(test_app): +def test_collapse_button_in_variant_doc(test_app, test_server): """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" app = test_app app.build() @@ -34,12 +34,12 @@ def test_collapse_button_in_variant_doc(test_app): { "buildername": "html", "srcdir": "doc_test/doc_basic", - "spec_pattern": "js_test/js-test-sn-collapse-button.cy.js", + "spec_pattern": "js_test/sn-collapse-button.cy.js", } ], indirect=True, ) -def test_collapse_button_in_doc_basic(test_app): +def test_collapse_button_in_doc_basic(test_app, test_server): """Check if the Sphinx-Needs collapse button works in the provided documentation source.""" app = test_app app.build() From b724f103d339db9b836c35e0cc3dcdc470bd86da Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Thu, 28 Sep 2023 08:58:03 +0000 Subject: [PATCH 05/14] Updated conftest.py --- tests/conftest.py | 64 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 53 insertions(+), 11 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 83c23988e..1d1ce698b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -9,6 +9,7 @@ from typing import Any, Dict import pytest +import xprocess from sphinx.application import Sphinx from sphinx.testing.path import path from xprocess import ProcessStarter @@ -17,11 +18,28 @@ def generate_random_string() -> str: + """ + Generate a random string of 10 characters consisting of letters (both uppercase and lowercase) and digits. + + :return: A random string. + """ characters = string.ascii_letters + string.digits return "".join(secrets.choice(characters) for i in range(10)) def copy_srcdir_to_tmpdir(srcdir: path, tmp: path) -> path: + """ + Copy Source Directory to Temporary Directory. + + This function copies the contents of a source directory to a temporary + directory. It generates a random subdirectory within the temporary directory + to avoid conflicts and enable parallel processes to run without conflicts. + + :param srcdir: Path to the source directory. + :param tmp: Path to the temporary directory. + + :return: Path to the newly created directory in the temporary directory. + """ srcdir = path(__file__).parent.abspath() / srcdir tmproot = tmp.joinpath(generate_random_string()) / path(srcdir).basename() shutil.copytree(srcdir, tmproot) @@ -44,28 +62,30 @@ def get_abspath(relpath: str) -> str: @pytest.fixture(scope="session") -def test_server(xprocess, sphinx_test_tempdir): +def test_server(sphinx_test_tempdir): + """ + Fixture to start and manage the test server process. + + :param sphinx_test_tempdir: The directory to serve. + :return: Information about the server process. + """ + class Starter(ProcessStarter): - # pattern = "HTTP Server started on port [0-9]+!" pattern = "Serving HTTP on [0-9.]+ port [0-9]+" - # wait for 10 seconds before timing out timeout = 10 - # terminate and clean up all started processes and their resources upon interruptions - # during pytest runs (CTRL+C, SIGINT and internal errors) terminate_on_interrupt = True - # command to start process - # args = ["python", f"{path(__file__).parent.abspath()}/server.py", sphinx_test_tempdir] args = ["python", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", "127.0.0.1", "62343"] - # ensure process is running and return its logfile - logfile = xprocess.ensure("http_server", Starter, restart=True) # noqa:F841 + # Start the process and ensure it is running + logfile = xprocess.ensure("http_server", Starter) # noqa:F841 + http_server_process = xprocess.getinfo("http_server") server_url = "http://localhost:62343" http_server_process.url = server_url yield http_server_process - # clean up whole process tree afterward + # Clean up the process after the tests http_server_process.terminate() @@ -134,7 +154,15 @@ def test_js(self) -> Dict[str, Any]: @pytest.fixture(scope="session") -def sphinx_test_tempdir(): +def sphinx_test_tempdir() -> path: + """ + Fixture to provide a temporary directory for Sphinx testing. + + This function creates a custom temporary folder to avoid potential conflicts + with utility functions from Sphinx and pytest. + + :return Path: Path object representing the temporary directory. + """ # We create a temp-folder on our own, as the util-functions from sphinx and pytest make troubles. # It seems like they reuse certain-temp names sphinx_test_tempdir = path(tempfile.gettempdir()).joinpath("sn_test_build_data") @@ -155,6 +183,20 @@ def sphinx_test_tempdir(): @pytest.fixture(scope="function") def test_app(make_app, sphinx_test_tempdir, request): + """ + Fixture for creating a Sphinx application for testing. + + This fixture creates a Sphinx application with specified builder parameters and + config overrides. It also copies the test source directory to the test temporary + directory. The fixture yields the Sphinx application, and cleans up the temporary + source directory after the test function has executed. + + :param make_app: A fixture for creating Sphinx applications. + :param sphinx_test_tempdir: A fixture for providing the Sphinx test temporary directory. + :param request: A pytest request object for accessing fixture parameters. + + :return: A Sphinx application object. + """ builder_params = request.param sphinx_conf_overrides = builder_params.get("confoverrides", {}) From 3892d4d93889cf3a7d9ae9629acfcb99847f7538 Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Thu, 28 Sep 2023 09:10:41 +0000 Subject: [PATCH 06/14] Updated conftest.py --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5678f02b8..3f3c8b322 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ test: .PHONY: test test-short: - poetry run pytest -n auto -m "jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ .PHONY: benchmark-time benchmark-time: From 3d860a2a6ae2de748fc1be88ddd5ef78208e433a Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Thu, 28 Sep 2023 09:11:42 +0000 Subject: [PATCH 07/14] Updated conftest.py and requirements.txt --- docs/requirements.txt | 1 + tests/conftest.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index ed19d3bf9..b0a0945f6 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -8,3 +8,4 @@ sphinx-design click tabulate sphinx-immaterial==0.11.7 +pytest-xprocess \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index fa27332e1..c9f8f684d 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -8,8 +8,8 @@ from pathlib import Path from typing import Any, Dict -from docutils.nodes import document import pytest +from docutils.nodes import document from sphinx.application import Sphinx from sphinx.testing.path import path from syrupy.extensions.single_file import SingleFileSnapshotExtension, WriteMode @@ -63,7 +63,7 @@ def get_abspath(relpath: str) -> str: @pytest.fixture(scope="session") -def test_server(sphinx_test_tempdir): +def test_server(xprocess, sphinx_test_tempdir): """ Fixture to start and manage the test server process. From cd5cb083c0859773792d63b57a4f749df986ca73 Mon Sep 17 00:00:00 2001 From: Duodu Randy Date: Tue, 10 Oct 2023 16:32:43 +0000 Subject: [PATCH 08/14] Updated conftest.py and Makefile --- Makefile | 2 +- tests/conftest.py | 27 +++++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 3f3c8b322..2261a8ac1 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ test: .PHONY: test test-short: - poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + poetry run pytest -n auto --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ .PHONY: benchmark-time benchmark-time: diff --git a/tests/conftest.py b/tests/conftest.py index c9f8f684d..e716f6546 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,6 +3,7 @@ import os.path import secrets import shutil +import socket import string import tempfile from pathlib import Path @@ -70,18 +71,32 @@ def test_server(xprocess, sphinx_test_tempdir): :param sphinx_test_tempdir: The directory to serve. :return: Information about the server process. """ + addr = "127.0.0.1" + port = 62343 class Starter(ProcessStarter): - pattern = "Serving HTTP on [0-9.]+ port [0-9]+" - timeout = 10 + pattern = "Serving HTTP" + timeout = 20 terminate_on_interrupt = True - args = ["python", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", "127.0.0.1", "62343"] + args = ["python", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", addr, port] - # Start the process and ensure it is running - logfile = xprocess.ensure("http_server", Starter) # noqa:F841 + # checks if our server is ready with a ping + + def check_server_connection(): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((addr, port)) + sock.close() + if result == 0: + print("Serving HTTP on 127.0.0.1 port 62343 (http://127.0.0.1:62343/) ...") + return True + return False http_server_process = xprocess.getinfo("http_server") - server_url = "http://localhost:62343" + if not check_server_connection(): + # Start the process and ensure it is running + logfile = xprocess.ensure("http_server", Starter) # noqa:F841 + + server_url = f"http://{addr}:{port}" http_server_process.url = server_url yield http_server_process From c01558e5351d6a6df7e5ae82706e28940503a4c4 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 28 Sep 2023 07:22:05 -0300 Subject: [PATCH 09/14] =?UTF-8?q?=E2=9C=A8=20Add=20error=20messages=20for?= =?UTF-8?q?=20constraint=20failures=20(#1036)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds the ability to set an optional `error_message` key for each constraint in the `needs_constraint` configuration. This can contain Jinja placeholders, for needs data fields, and will be added to the need data, under the `constraints_error` field, if the constraint fails. --- docs/configuration.rst | 16 + sphinx_needs/config.py | 7 +- sphinx_needs/data.py | 5 +- sphinx_needs/directives/need.py | 19 +- sphinx_needs/need_constraints.py | 160 +++-- tests/__snapshots__/test_external.ambr | 4 +- tests/__snapshots__/test_import.ambr | 34 +- .../__snapshots__/test_need_constraints.ambr | 611 ++++++++++++++++++ tests/doc_test/need_constraints/conf.py | 8 +- tests/test_need_constraints.py | 8 +- 10 files changed, 761 insertions(+), 111 deletions(-) create mode 100644 tests/__snapshots__/test_need_constraints.ambr diff --git a/docs/configuration.rst b/docs/configuration.rst index 0ed810299..ad230cae1 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -1939,6 +1939,22 @@ constraints_passed is a bool showing if ALL constraints of a corresponding need constraints_results is a dictionary similar in structure to needs_constraints above. Instead of executable python statements, inner values contain a bool describing if check_0, check_1 ... passed successfully. +.. versionadded:: 1.4.0 + + The ``"error_message"`` key can contain a string, with Jinja templating, which will be displayed if the constraint fails, and saved on the need as ``constraints_error``: + + .. code-block:: python + + needs_constraints = { + + "critical": { + "check_0": "'critical' in tags", + "severity": "CRITICAL", + "error_message": "need {% raw %}{{id}}{% endraw %} does not fulfill CRITICAL constraint, because tags are {% raw %}{{tags}}{% endraw %}" + } + + } + .. code-block:: rst diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index 1e29ec713..da452c424 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -232,7 +232,12 @@ def __setattr__(self, name: str, value: Any) -> None: """path to needs_report_template file which is based on the conf.py directory.""" constraints: dict[str, dict[str, str]] = field(default_factory=dict, metadata={"rebuild": "html", "types": (dict,)}) - """Mapping of constraint name, to check name, to filter string.""" + """Mapping of constraint name, to check name, to filter string. + There are also some special keys for a constraint: + + - severity: The severity of the constraint. This is used to determine what to do if the constraint is not fulfilled. + - error_message: A help text for the constraint, can include Jinja2 variables. + """ constraint_failed_options: dict[str, ConstraintFailedType] = field( default_factory=dict, metadata={"rebuild": "html", "types": (dict,)} ) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 521c169cb..3c8ddaa7d 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -44,6 +44,7 @@ class NeedsWorkflowType(TypedDict): add_sections: bool variant_option_resolved: bool needs_extended: bool + needs_constraints: bool class NeedsBaseDataType(TypedDict): @@ -166,6 +167,8 @@ class NeedsInfoType(NeedsBaseDataType): """Mapping of constraint name, to check name, to result.""" constraints_passed: None | bool """True if all constraints passed, False if any failed, None if not yet checked.""" + constraints_error: str + """An error message set if any constraint failed, and `error_message` field is set in config.""" # additional source information doctype: str @@ -460,8 +463,8 @@ def get_or_create_workflow(self) -> NeedsWorkflowType: "add_sections": False, "variant_option_resolved": False, "needs_extended": False, + "needs_constraints": False, } - # TODO use needs_config here for link_type in self.env.app.config.needs_extra_links: self.env.needs_workflow["backlink_creation_{}".format(link_type["option"])] = False diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 3f4385a95..7a0987fb6 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -394,28 +394,13 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - for links in needs_config.extra_links: create_back_links(env, links["option"]) - """ - The output of this phase is a doctree for each source file; that is a tree of docutils nodes. - - https://www.sphinx-doc.org/en/master/extdev/index.html - - """ - needs = SphinxNeedsData(env).get_or_create_needs() - - # Used to store needs in the docs, which are needed again later - found_needs_nodes = [] - for node_need in doctree.findall(Need): - need_id = node_need.attributes["ids"][0] - found_needs_nodes.append(node_need) - need_data = needs[need_id] - - process_constraints(app, need_data) + process_constraints(app) # We call process_needextend here by our own, so that we are able # to give print_need_nodes the already found need_nodes. process_needextend(app, doctree, fromdocname) - print_need_nodes(app, doctree, fromdocname, found_needs_nodes) + print_need_nodes(app, doctree, fromdocname, list(doctree.findall(Need))) @profile("NEED_PRINT") diff --git a/sphinx_needs/need_constraints.py b/sphinx_needs/need_constraints.py index 0cee5bc2d..910526f22 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -1,86 +1,106 @@ +from typing import Dict + +import jinja2 from sphinx.application import Sphinx from sphinx_needs.api.exceptions import NeedsConstraintFailed, NeedsConstraintNotAllowed from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import NeedsInfoType +from sphinx_needs.data import SphinxNeedsData from sphinx_needs.filter_common import filter_single_need from sphinx_needs.logging import get_logger logger = get_logger(__name__) -def process_constraints(app: Sphinx, need: NeedsInfoType) -> None: +def process_constraints(app: Sphinx) -> None: """Analyse constraints of a single need, and set corresponding fields on the need data item. """ - needs_config = NeedsSphinxConfig(app.config) + env = app.env + needs_config = NeedsSphinxConfig(env.config) config_constraints = needs_config.constraints - need_id = need["id"] - constraints = need["constraints"] - - # flag that is set to False if any check fails - need["constraints_passed"] = True - - for constraint in constraints: - try: - executable_constraints = config_constraints[constraint] - except KeyError: - # Note, this is already checked for in add_need - raise NeedsConstraintNotAllowed( - f"Constraint {constraint} of need id {need_id} is not allowed by config value 'needs_constraints'." - ) - - # name is check_0, check_1, ... - for name, cmd in executable_constraints.items(): - if name == "severity": - # special key, that is not a check - continue - - # compile constraint and check if need fulfils it - constraint_passed = filter_single_need(app, need, cmd) - - if constraint_passed: - need["constraints_results"].setdefault(constraint, {})[name] = True - else: - need["constraints_results"].setdefault(constraint, {})[name] = False - need["constraints_passed"] = False - - if "severity" not in executable_constraints: - raise NeedsConstraintFailed( - f"'severity' key not set for constraint {constraint!r} in config 'needs_constraints'" - ) - severity = executable_constraints["severity"] - if severity not in needs_config.constraint_failed_options: - raise NeedsConstraintFailed( - f"Severity {severity!r} not set in config 'needs_constraint_failed_options'" - ) - failed_options = needs_config.constraint_failed_options[severity] - - # log/except if needed - if "warn" in failed_options.get("on_fail", []): - logger.warning( - f"Constraint {cmd} for need {need_id} FAILED! severity: {severity} [needs.constraint]", - type="needs", - subtype="constraint", - color="red", - location=(need["docname"], need["lineno"]), - ) - if "break" in failed_options.get("on_fail", []): - raise NeedsConstraintFailed( - f"FAILED a breaking constraint: >> {cmd} << for need " - f"{need_id} FAILED! breaking build process" - ) - - # set styles - old_style = need["style"] - if old_style and len(old_style) > 0: - new_styles = "".join(", " + x for x in failed_options.get("style", [])) - else: - old_style = "" - new_styles = "".join(x + "," for x in failed_options.get("style", [])) + needs = SphinxNeedsData(env).get_or_create_needs() + workflow = SphinxNeedsData(env).get_or_create_workflow() + + if workflow["needs_constraints"]: + return + + workflow["needs_constraints"] = True + + error_templates_cache: Dict[str, jinja2.Template] = {} + + for need in needs.values(): + need_id = need["id"] + constraints = need["constraints"] + + # flag that is set to False if any check fails + need["constraints_passed"] = True + + for constraint in constraints: + try: + executable_constraints = config_constraints[constraint] + except KeyError: + # Note, this is already checked for in add_need + raise NeedsConstraintNotAllowed( + f"Constraint {constraint} of need id {need_id} is not allowed by config value 'needs_constraints'." + ) - if failed_options.get("force_style", False): - need["style"] = new_styles.strip(", ") + # name is check_0, check_1, ... + for name, cmd in executable_constraints.items(): + if name in ("severity", "error_message"): + # special keys, that are not a check + continue + + # compile constraint and check if need fulfils it + constraint_passed = filter_single_need(app, need, cmd) + + if constraint_passed: + need["constraints_results"].setdefault(constraint, {})[name] = True else: - constraint_failed_style = old_style + new_styles - need["style"] = constraint_failed_style + need["constraints_results"].setdefault(constraint, {})[name] = False + need["constraints_passed"] = False + + if "error_message" in executable_constraints: + msg = str(executable_constraints["error_message"]) + template = error_templates_cache.setdefault(msg, jinja2.Template(msg)) + need["constraints_error"] = template.render(**need) + + if "severity" not in executable_constraints: + raise NeedsConstraintFailed( + f"'severity' key not set for constraint {constraint!r} in config 'needs_constraints'" + ) + severity = executable_constraints["severity"] + if severity not in needs_config.constraint_failed_options: + raise NeedsConstraintFailed( + f"Severity {severity!r} not set in config 'needs_constraint_failed_options'" + ) + failed_options = needs_config.constraint_failed_options[severity] + + # log/except if needed + if "warn" in failed_options.get("on_fail", []): + logger.warning( + f"Constraint {cmd} for need {need_id} FAILED! severity: {severity} {need.get('constraints_error', '')} [needs.constraint]", + type="needs", + subtype="constraint", + color="red", + location=(need["docname"], need["lineno"]), + ) + if "break" in failed_options.get("on_fail", []): + raise NeedsConstraintFailed( + f"FAILED a breaking constraint: >> {cmd} << for need " + f"{need_id} FAILED! breaking build process" + ) + + # set styles + old_style = need["style"] + if old_style and len(old_style) > 0: + new_styles = "".join(", " + x for x in failed_options.get("style", [])) + else: + old_style = "" + new_styles = "".join(x + "," for x in failed_options.get("style", [])) + + if failed_options.get("force_style", False): + need["style"] = new_styles.strip(", ") + else: + constraint_failed_style = old_style + new_styles + need["style"] = constraint_failed_style diff --git a/tests/__snapshots__/test_external.ambr b/tests/__snapshots__/test_external.ambr index 95958c496..04d9a5e6c 100644 --- a/tests/__snapshots__/test_external.ambr +++ b/tests/__snapshots__/test_external.ambr @@ -17,7 +17,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -89,7 +89,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, diff --git a/tests/__snapshots__/test_import.ambr b/tests/__snapshots__/test_import.ambr index e741620a8..017f2081d 100644 --- a/tests/__snapshots__/test_import.ambr +++ b/tests/__snapshots__/test_import.ambr @@ -1103,7 +1103,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2178,7 +2178,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2261,7 +2261,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2336,7 +2336,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2411,7 +2411,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2489,7 +2489,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2563,7 +2563,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2638,7 +2638,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2714,7 +2714,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2790,7 +2790,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2871,7 +2871,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -2947,7 +2947,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -3030,7 +3030,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -3106,7 +3106,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -3179,7 +3179,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -3255,7 +3255,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, @@ -4673,7 +4673,7 @@ 'completion': '', 'constraints': list([ ]), - 'constraints_passed': None, + 'constraints_passed': True, 'constraints_results': dict({ }), 'content_id': None, diff --git a/tests/__snapshots__/test_need_constraints.ambr b/tests/__snapshots__/test_need_constraints.ambr new file mode 100644 index 000000000..8334cd384 --- /dev/null +++ b/tests/__snapshots__/test_need_constraints.ambr @@ -0,0 +1,611 @@ +# serializer version: 1 +# name: test_need_constraints[test_app0] + dict({ + 'current_version': '', + 'project': 'Python', + 'versions': dict({ + '': dict({ + 'filters': dict({ + }), + 'filters_amount': 0, + 'needs': dict({ + 'SECURITY_REQ': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'SECURITY_REQ', + 'created_at': '', + 'delete': None, + 'description': 'This is a requirement describing OPSEC processes.', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'test1', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SECURITY_REQ', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': None, + 'tags': list([ + ]), + 'target_id': 'SECURITY_REQ', + 'template': None, + 'title': 'test1', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'SP_109F4': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'critical', + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + 'critical': dict({ + 'check_0': True, + }), + }), + 'content_id': 'SP_109F4', + 'created_at': '', + 'delete': None, + 'description': 'Example of a successful constraint.', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'test2', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SP_109F4', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + 'SECURITY_REQ', + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': None, + 'tags': list([ + 'critical', + ]), + 'target_id': 'SP_109F4', + 'template': None, + 'title': 'test2', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'SP_3EBFA': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'critical', + ]), + 'constraints_error': 'need SP_3EBFA does not fulfill CRITICAL constraint, because tags are []', + 'constraints_passed': False, + 'constraints_results': dict({ + 'critical': dict({ + 'check_0': False, + }), + }), + 'content_id': 'SP_3EBFA', + 'created_at': '', + 'delete': None, + 'description': 'Example of a failed constraint.', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'test3', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SP_3EBFA', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': 'debug', + 'links': list([ + 'SECURITY_REQ', + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': 'red_bar', + 'tags': list([ + ]), + 'target_id': 'SP_3EBFA', + 'template': None, + 'title': 'test3', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'SP_CA3FB': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'team', + ]), + 'constraints_passed': False, + 'constraints_results': dict({ + 'team': dict({ + 'check_0': False, + }), + }), + 'content_id': 'SP_CA3FB', + 'created_at': '', + 'delete': None, + 'description': 'Example of a failed constraint with medium severity. Note the style from :ref:`needs_constraint_failed_options`', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'FAIL_01', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SP_CA3FB', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': 'yellow_bar,', + 'tags': list([ + ]), + 'target_id': 'SP_CA3FB', + 'template': None, + 'title': 'FAIL_01', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'SP_TOO_001': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + ]), + 'constraints_passed': True, + 'constraints_results': dict({ + }), + 'content_id': 'SP_TOO_001', + 'created_at': '', + 'delete': None, + 'description': 'The Tool awesome shall have a command line interface.', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'Command line interface', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SP_TOO_001', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': 'implemented', + 'style': None, + 'tags': list([ + 'test', + 'test2', + ]), + 'target_id': 'SP_TOO_001', + 'template': None, + 'title': 'Command line interface', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'SP_TOO_002': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'critical', + ]), + 'constraints_error': "need SP_TOO_002 does not fulfill CRITICAL constraint, because tags are ['hello', 'there']", + 'constraints_passed': False, + 'constraints_results': dict({ + 'critical': dict({ + 'check_0': False, + }), + }), + 'content_id': 'SP_TOO_002', + 'created_at': '', + 'delete': None, + 'description': 'asdf', + 'docname': 'index', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'CLI', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'SP_TOO_002', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': 'dev', + 'style': 'red_bar', + 'tags': list([ + 'hello', + 'there', + ]), + 'target_id': 'SP_TOO_002', + 'template': None, + 'title': 'CLI', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'TEST_STYLE': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'critical', + ]), + 'constraints_error': 'need TEST_STYLE does not fulfill CRITICAL constraint, because tags are []', + 'constraints_passed': False, + 'constraints_results': dict({ + 'critical': dict({ + 'check_0': False, + }), + }), + 'content_id': 'TEST_STYLE', + 'created_at': '', + 'delete': None, + 'description': '', + 'docname': 'style_test', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'CLI', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'TEST_STYLE', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': 'red_bar', + 'tags': list([ + ]), + 'target_id': 'TEST_STYLE', + 'template': None, + 'title': 'CLI', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + 'TEST_STYLE2': dict({ + 'arch': dict({ + }), + 'avatar': '', + 'closed_at': '', + 'completion': '', + 'constraints': list([ + 'team', + ]), + 'constraints_passed': False, + 'constraints_results': dict({ + 'team': dict({ + 'check_0': False, + }), + }), + 'content_id': 'TEST_STYLE2', + 'created_at': '', + 'delete': None, + 'description': '', + 'docname': 'style_test', + 'doctype': '.rst', + 'duration': '', + 'external_css': 'external_link', + 'external_url': None, + 'full_title': 'CLI2', + 'has_dead_links': '', + 'has_forbidden_dead_links': '', + 'hidden': '', + 'id': 'TEST_STYLE2', + 'id_prefix': '', + 'is_external': False, + 'is_modified': False, + 'is_need': True, + 'is_part': False, + 'jinja_content': None, + 'layout': '', + 'links': list([ + ]), + 'max_amount': '', + 'max_content_lines': '', + 'modifications': 0, + 'params': '', + 'parent_need': '', + 'parent_needs': list([ + ]), + 'parent_needs_back': list([ + ]), + 'parts': dict({ + }), + 'post_template': None, + 'pre_template': None, + 'prefix': '', + 'query': '', + 'section_name': 'TEST DOCUMENT NEEDS CONSTRAINTS', + 'sections': list([ + 'TEST DOCUMENT NEEDS CONSTRAINTS', + ]), + 'service': '', + 'signature': '', + 'specific': '', + 'status': None, + 'style': 'blue_border, yellow_bar', + 'tags': list([ + ]), + 'target_id': 'TEST_STYLE2', + 'template': None, + 'title': 'CLI2', + 'type': 'spec', + 'type_name': 'Specification', + 'updated_at': '', + 'url': '', + 'url_postfix': '', + 'user': '', + }), + }), + 'needs_amount': 8, + }), + }), + }) +# --- diff --git a/tests/doc_test/need_constraints/conf.py b/tests/doc_test/need_constraints/conf.py index a2a50da0b..acbee6adf 100644 --- a/tests/doc_test/need_constraints/conf.py +++ b/tests/doc_test/need_constraints/conf.py @@ -2,6 +2,7 @@ extensions = ["sphinx_needs"] +needs_build_json = True needs_table_style = "TABLE" needs_types = [ @@ -54,11 +55,14 @@ def setup(app): needs_extra_options = [] - needs_constraints = { "security": {"check_0": "'security' in tags", "severity": "CRITICAL"}, "team": {"check_0": "'team_requirement' in links", "severity": "MEDIUM"}, - "critical": {"check_0": "'critical' in tags", "severity": "CRITICAL"}, + "critical": { + "check_0": "'critical' in tags", + "severity": "CRITICAL", + "error_message": "need {{id}} does not fulfill CRITICAL constraint, because tags are {{tags}}", + }, } diff --git a/tests/test_need_constraints.py b/tests/test_need_constraints.py index f55f2fe95..72e869dfa 100644 --- a/tests/test_need_constraints.py +++ b/tests/test_need_constraints.py @@ -1,16 +1,22 @@ +import json import subprocess from pathlib import Path import pytest +from syrupy.filters import props from sphinx_needs.api.exceptions import NeedsConstraintNotAllowed @pytest.mark.parametrize("test_app", [{"buildername": "html", "srcdir": "doc_test/need_constraints"}], indirect=True) -def test_need_constraints(test_app): +def test_need_constraints(test_app, snapshot): app = test_app app.build() + json_text = Path(app.outdir, "needs.json").read_text() + needs_data = json.loads(json_text) + assert needs_data == snapshot(exclude=props("created")) + srcdir = Path(app.srcdir) out_dir = srcdir / "_build" From 5c32994ba35ca1ee64e6db4e3e835e8db781748e Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Thu, 28 Sep 2023 10:59:28 -0300 Subject: [PATCH 10/14] =?UTF-8?q?=F0=9F=94=A7=20Consolidate=20needs=20data?= =?UTF-8?q?=20post-processing=20(#1039)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit is a step towards being able to generate the needs.json without running the Sphinx `BUILD` phase. Here we consolidate all functions that post-process the needs data, after it has been fully extracted from all documents and external sources. We remove the individual logic from within these functions, to check if the post-processing has already been done, and instead check before running all the functions. This refactor does not actually change the sphinx-needs process in any way, this will come later. --- docs/contributing.rst | 2 +- sphinx_needs/data.py | 46 ++---- sphinx_needs/directives/need.py | 114 ++++++------- sphinx_needs/directives/needextend.py | 227 ++++++++++++-------------- sphinx_needs/functions/functions.py | 60 +++---- sphinx_needs/need_constraints.py | 14 +- sphinx_needs/needs.py | 2 - 7 files changed, 197 insertions(+), 268 deletions(-) diff --git a/docs/contributing.rst b/docs/contributing.rst index df6506190..f0f71cbbf 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -342,7 +342,7 @@ The following is an outline of the build events which this extension adds to the - Check for dead links (``process_need_nodes -> check_links``) - Generate back links (``process_need_nodes -> create_back_links``) - Process constraints, for each ``Need`` node (``process_need_nodes -> process_constraints``) - - Perform all modifications on need data items, due to ``Needextend`` nodes (``process_need_nodes -> process_needextend``) + - Perform all modifications on need data items, due to ``Needextend`` nodes (``process_need_nodes -> extend_needs_data``) - Format each ``Need`` node to give the desired visual output (``process_need_nodes -> print_need_nodes``) - Process all other need specific nodes, replacing them with the desired visual output (``process_creator``) diff --git a/sphinx_needs/data.py b/sphinx_needs/data.py index 3c8ddaa7d..9fa8d44f2 100644 --- a/sphinx_needs/data.py +++ b/sphinx_needs/data.py @@ -30,23 +30,6 @@ class NeedsFilterType(TypedDict): amount: int -class NeedsWorkflowType(TypedDict): - """ - Used to store workflow status information for already executed tasks. - Some tasks like backlink_creation need be performed only once. - But most sphinx-events get called several times (for each single document file), - which would also execute our code several times... - """ - - backlink_creation_links: bool - dynamic_values_resolved: bool - links_checked: bool - add_sections: bool - variant_option_resolved: bool - needs_extended: bool - needs_constraints: bool - - class NeedsBaseDataType(TypedDict): """A base type for all data.""" @@ -448,27 +431,18 @@ def get_or_create_docs(self) -> dict[str, list[str]]: self.env.needs_all_docs = {"all": []} return self.env.needs_all_docs - def get_or_create_workflow(self) -> NeedsWorkflowType: - """Get workflow information. - - This is lazily created and cached in the environment. - """ + @property + def needs_is_post_processed(self) -> bool: + """Whether needs have been post-processed.""" try: - return self.env.needs_workflow + return self.env.needs_is_post_processed except AttributeError: - self.env.needs_workflow = { - "backlink_creation_links": False, - "dynamic_values_resolved": False, - "links_checked": False, - "add_sections": False, - "variant_option_resolved": False, - "needs_extended": False, - "needs_constraints": False, - } - for link_type in self.env.app.config.needs_extra_links: - self.env.needs_workflow["backlink_creation_{}".format(link_type["option"])] = False - - return self.env.needs_workflow # type: ignore[return-value] + self.env.needs_is_post_processed = False + return self.env.needs_is_post_processed + + @needs_is_post_processed.setter + def needs_is_post_processed(self, value: bool) -> None: + self.env.needs_is_post_processed = value def get_or_create_services(self) -> ServiceManager: """Get information about services. diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 7a0987fb6..3895bdf5a 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -17,7 +17,11 @@ from sphinx_needs.data import SphinxNeedsData from sphinx_needs.debug import measure_time from sphinx_needs.defaults import NEED_DEFAULT_OPTIONS -from sphinx_needs.directives.needextend import process_needextend +from sphinx_needs.directives.needextend import ( + Needextend, + extend_needs_data, + remove_needextend_node, +) from sphinx_needs.functions import ( find_and_replace_node_content, resolve_dynamic_values, @@ -376,29 +380,23 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - return env = app.env + needs_data = SphinxNeedsData(env) # If no needs were defined, we do not need to do anything - if not hasattr(env, "needs_all_needs"): + if not needs_data.get_or_create_needs(): return - # Call dynamic functions and replace related node data with their return values - resolve_dynamic_values(env) - - # Apply variant handling on options and replace its values with their return values - resolve_variants_options(env) - - # check if we have dead links - check_links(env) - - # Create back links of common links and extra links - for links in needs_config.extra_links: - create_back_links(env, links["option"]) - - process_constraints(app) + if not needs_data.needs_is_post_processed: + resolve_dynamic_values(env) + resolve_variants_options(env) + check_links(env) + create_back_links(env) + process_constraints(app) + extend_needs_data(app) + needs_data.needs_is_post_processed = True - # We call process_needextend here by our own, so that we are able - # to give print_need_nodes the already found need_nodes. - process_needextend(app, doctree, fromdocname) + for extend_node in doctree.findall(Needextend): + remove_needextend_node(extend_node) print_need_nodes(app, doctree, fromdocname, list(doctree.findall(Need))) @@ -432,18 +430,16 @@ def print_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, fou def check_links(env: BuildEnvironment) -> None: + """Checks if set links are valid or are dead (referenced need does not exist.) + + For needs with dead links, an extra ``has_dead_links`` field is added and, + if the link is not allowed to be dead, + the ``has_forbidden_dead_links`` field is also added. """ - Checks if set links are valid or are dead (referenced need does not exist.) - :param env: Sphinx environment - :return: - """ + config = NeedsSphinxConfig(env.config) data = SphinxNeedsData(env) - workflow = data.get_or_create_workflow() - if workflow["links_checked"]: - return - needs = data.get_or_create_needs() - extra_links = getattr(env.config, "needs_extra_links", []) + extra_links = config.extra_links for need in needs.values(): for link_type in extra_links: dead_links_allowed = link_type.get("allow_dead_links", False) @@ -464,45 +460,39 @@ def check_links(env: BuildEnvironment) -> None: need["has_forbidden_dead_links"] = True break # One found dead link is enough - # Finally set a flag so that this function gets not executed several times - workflow["links_checked"] = True - -def create_back_links(env: BuildEnvironment, option: str) -> None: - """ - Create back-links in all found needs. - But do this only once, as all needs are already collected and this sorting is for all - needs and not only for the ones of the current document. +def create_back_links(env: BuildEnvironment) -> None: + """Create back-links in all found needs. - :param env: sphinx environment + These are fields for each link type, ``_back``, + which contain a list of all IDs of needs that link to the current need. """ data = SphinxNeedsData(env) - workflow = data.get_or_create_workflow() - option_back = f"{option}_back" - if workflow[f"backlink_creation_{option}"]: # type: ignore[literal-required] - return - + needs_config = NeedsSphinxConfig(env.config) needs = data.get_or_create_needs() - for key, need in needs.items(): - need_link_value = [need[option]] if isinstance(need[option], str) else need[option] # type: ignore[literal-required] - for link in need_link_value: - link_main = link.split(".")[0] - try: - link_part = link.split(".")[1] - except IndexError: - link_part = None - - if link_main in needs: - if key not in needs[link_main][option_back]: # type: ignore[literal-required] - needs[link_main][option_back].append(key) # type: ignore[literal-required] - - # Handling of links to need_parts inside a need - if link_part and link_part in needs[link_main]["parts"]: - if option_back not in needs[link_main]["parts"][link_part].keys(): - needs[link_main]["parts"][link_part][option_back] = [] # type: ignore[literal-required] - needs[link_main]["parts"][link_part][option_back].append(key) # type: ignore[literal-required] - - workflow[f"backlink_creation_{option}"] = True # type: ignore[literal-required] + + for links in needs_config.extra_links: + option = links["option"] + option_back = f"{option}_back" + + for key, need in needs.items(): + need_link_value = [need[option]] if isinstance(need[option], str) else need[option] # type: ignore[literal-required] + for link in need_link_value: + link_main = link.split(".")[0] + try: + link_part = link.split(".")[1] + except IndexError: + link_part = None + + if link_main in needs: + if key not in needs[link_main][option_back]: # type: ignore[literal-required] + needs[link_main][option_back].append(key) # type: ignore[literal-required] + + # Handling of links to need_parts inside a need + if link_part and link_part in needs[link_main]["parts"]: + if option_back not in needs[link_main]["parts"][link_part].keys(): + needs[link_main]["parts"][link_part][option_back] = [] # type: ignore[literal-required] + needs[link_main]["parts"][link_part][option_back].append(key) # type: ignore[literal-required] def _fix_list_dyn_func(list: List[str]) -> List[str]: diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 2b10fddb3..278593864 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -71,132 +71,119 @@ def run(self) -> Sequence[nodes.Node]: return [targetnode, Needextend("")] -def process_needextend(app: Sphinx, doctree: nodes.document, fromdocname: str) -> None: - """ - Perform all modifications on needs - """ +def extend_needs_data(app: Sphinx) -> None: + """Use data gathered from needextend directives to modify fields of existing needs.""" env = app.env needs_config = NeedsSphinxConfig(env.config) data = SphinxNeedsData(env) - workflow = data.get_or_create_workflow() - - if not workflow["needs_extended"]: - workflow["needs_extended"] = True - - list_values = ( - ["tags", "links"] - + [x["option"] for x in needs_config.extra_links] - + [f"{x['option']}_back" for x in needs_config.extra_links] - ) # back-links (incoming) - link_names = [x["option"] for x in needs_config.extra_links] - - all_needs = data.get_or_create_needs() - - for current_needextend in data.get_or_create_extends().values(): - need_filter = current_needextend["filter"] - if need_filter in all_needs: - # a single known ID - found_needs = [all_needs[need_filter]] - elif need_filter is not None and re.fullmatch(needs_config.id_regex, need_filter): - # an unknown ID - error = f"Provided id {need_filter} for needextend does not exist." - if current_needextend["strict"]: - raise NeedsInvalidFilter(error) - else: - logger.info(error) - continue + + list_values = ( + ["tags", "links"] + + [x["option"] for x in needs_config.extra_links] + + [f"{x['option']}_back" for x in needs_config.extra_links] + ) # back-links (incoming) + link_names = [x["option"] for x in needs_config.extra_links] + + all_needs = data.get_or_create_needs() + + for current_needextend in data.get_or_create_extends().values(): + need_filter = current_needextend["filter"] + if need_filter in all_needs: + # a single known ID + found_needs = [all_needs[need_filter]] + elif need_filter is not None and re.fullmatch(needs_config.id_regex, need_filter): + # an unknown ID + error = f"Provided id {need_filter} for needextend does not exist." + if current_needextend["strict"]: + raise NeedsInvalidFilter(error) else: - # a filter string - try: - found_needs = filter_needs(app, all_needs.values(), need_filter) - except NeedsInvalidFilter as e: - raise NeedsInvalidFilter( - f"Filter not valid for needextend on page {current_needextend['docname']}:\n{e}" - ) - - for found_need in found_needs: - # Work in the stored needs, not on the search result - need = all_needs[found_need["id"]] - need["is_modified"] = True - need["modifications"] += 1 - - for option, value in current_needextend["modifications"].items(): - if option.startswith("+"): - option_name = option[1:] - if option_name in link_names: - # If we add links, then add all corresponding back links - for ref_need in [i.strip() for i in re.split(";|,", value)]: - if ref_need not in all_needs: - logger.warning( - f"Provided link id {ref_need} for needextend does not exist. [needs]", - type="needs", - location=(current_needextend["docname"], current_needextend["lineno"]), - ) - continue - if ref_need not in need[option_name]: - need[option_name].append(ref_need) - if found_need["id"] not in all_needs[ref_need][f"{option_name}_back"]: - all_needs[ref_need][f"{option_name}_back"] += [found_need["id"]] - elif option_name in list_values: - for item in [i.strip() for i in re.split(";|,", value)]: - if item not in need[option_name]: - need[option_name].append(item) - else: - if need[option_name]: - # If content is already stored, we need to add some whitespace - need[option_name] += " " - need[option_name] += value - - elif option.startswith("-"): - option_name = option[1:] - if option_name in link_names: - # If we remove links, then remove all corresponding back links - for ref_need in (i for i in need[option_name] if i in all_needs): - all_needs[ref_need][f"{option_name}_back"].remove(found_need["id"]) - need[option_name] = [] - if option_name in list_values: - need[option_name] = [] - else: - need[option_name] = "" + logger.info(error) + continue + else: + # a filter string + try: + found_needs = filter_needs(app, all_needs.values(), need_filter) + except NeedsInvalidFilter as e: + raise NeedsInvalidFilter( + f"Filter not valid for needextend on page {current_needextend['docname']}:\n{e}" + ) + + for found_need in found_needs: + # Work in the stored needs, not on the search result + need = all_needs[found_need["id"]] + need["is_modified"] = True + need["modifications"] += 1 + + for option, value in current_needextend["modifications"].items(): + if option.startswith("+"): + option_name = option[1:] + if option_name in link_names: + # If we add links, then add all corresponding back links + for ref_need in [i.strip() for i in re.split(";|,", value)]: + if ref_need not in all_needs: + logger.warning( + f"Provided link id {ref_need} for needextend does not exist. [needs]", + type="needs", + location=(current_needextend["docname"], current_needextend["lineno"]), + ) + continue + if ref_need not in need[option_name]: + need[option_name].append(ref_need) + if found_need["id"] not in all_needs[ref_need][f"{option_name}_back"]: + all_needs[ref_need][f"{option_name}_back"] += [found_need["id"]] + elif option_name in list_values: + for item in [i.strip() for i in re.split(";|,", value)]: + if item not in need[option_name]: + need[option_name].append(item) + else: + if need[option_name]: + # If content is already stored, we need to add some whitespace + need[option_name] += " " + need[option_name] += value + + elif option.startswith("-"): + option_name = option[1:] + if option_name in link_names: + # If we remove links, then remove all corresponding back links + for ref_need in (i for i in need[option_name] if i in all_needs): + all_needs[ref_need][f"{option_name}_back"].remove(found_need["id"]) + need[option_name] = [] + if option_name in list_values: + need[option_name] = [] else: - if option in link_names: - # If we change links, then modify all corresponding back links - for ref_need in (i for i in need[option] if i in all_needs): - all_needs[ref_need][f"{option}_back"].remove(found_need["id"]) - need[option] = [] - for ref_need in [i.strip() for i in re.split(";|,", value)]: - if ref_need not in all_needs: - logger.warning( - f"Provided link id {ref_need} for needextend does not exist. [needs]", - type="needs", - location=(current_needextend["docname"], current_needextend["lineno"]), - ) - continue - need[option].append(ref_need) - for ref_need in need[option]: - if found_need["id"] not in all_needs[ref_need][f"{option}_back"]: - all_needs[ref_need][f"{option}_back"] += [found_need["id"]] - elif option in list_values: - need[option] = [i.strip() for i in re.split(";|,", value)] - else: - need[option] = value - - for node in doctree.findall(Needextend): - # No printouts for needextend - removed_needextend_node(node) - - -def removed_needextend_node(node: Needextend) -> None: + need[option_name] = "" + else: + if option in link_names: + # If we change links, then modify all corresponding back links + for ref_need in (i for i in need[option] if i in all_needs): + all_needs[ref_need][f"{option}_back"].remove(found_need["id"]) + need[option] = [] + for ref_need in [i.strip() for i in re.split(";|,", value)]: + if ref_need not in all_needs: + logger.warning( + f"Provided link id {ref_need} for needextend does not exist. [needs]", + type="needs", + location=(current_needextend["docname"], current_needextend["lineno"]), + ) + continue + need[option].append(ref_need) + for ref_need in need[option]: + if found_need["id"] not in all_needs[ref_need][f"{option}_back"]: + all_needs[ref_need][f"{option}_back"] += [found_need["id"]] + elif option in list_values: + need[option] = [i.strip() for i in re.split(";|,", value)] + else: + need[option] = value + + +def remove_needextend_node(node: Needextend) -> None: """ - # Remove needextend from docutils node-tree, so that no output gets generated for it. - # Ok, this is really dirty. - # If we replace a node, docutils checks, if it will not lose any attributes. - # But this is here the case, because we are using the attribute "ids" of a node. - # However, I do not understand, why losing an attribute is such a big deal, so we delete everything - # before docutils claims about it. - - :param node: - :return: + Remove needextend from docutils node-tree, so that no output gets generated for it. + Ok, this is really dirty. + If we replace a node, docutils checks, if it will not lose any attributes. + But this is here the case, because we are using the attribute "ids" of a node. + However, I do not understand, why losing an attribute is such a big deal, so we delete everything + before docutils claims about it. """ for att in ("ids", "names", "classes", "dupnames"): diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index f184bc2da..ab17613bc 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -151,25 +151,18 @@ def find_and_replace_node_content(node, env: BuildEnvironment, need): return node -def resolve_dynamic_values(env: BuildEnvironment): +def resolve_dynamic_values(env: BuildEnvironment) -> None: """ Resolve dynamic values inside need data. Rough workflow: - #. Parse all needs and their data for a string like [[ my_func(a,b,c) ]] + #. Parse all needs and their data for a string like ``[[ my_func(a,b,c) ]]`` #. Extract function name and call parameters #. Execute registered function name with extracted call parameters #. Replace original string with return value - - :param env: Sphinx environment - :return: return value of given function """ data = SphinxNeedsData(env) - workflow = data.get_or_create_workflow() - # Only perform calculation if not already done yet - if workflow["dynamic_values_resolved"]: - return needs = data.get_or_create_needs() for need in needs.values(): @@ -229,11 +222,8 @@ def resolve_dynamic_values(env: BuildEnvironment): need[need_option] = new_values - # Finally set a flag so that this function gets not executed several times - workflow["dynamic_values_resolved"] = True - -def resolve_variants_options(env: BuildEnvironment): +def resolve_variants_options(env: BuildEnvironment) -> None: """ Resolve variants options inside need data. @@ -241,38 +231,30 @@ def resolve_variants_options(env: BuildEnvironment): #. Parse all needs and their data for variant handling #. Replace original string with return value - :param env: Sphinx environment - :return: None """ data = SphinxNeedsData(env) - workflow = data.get_or_create_workflow() - # Only perform calculation if not already done yet - if workflow["variant_option_resolved"]: - return - needs_config = NeedsSphinxConfig(env.config) variants_options = needs_config.variant_options - if variants_options: - needs = data.get_or_create_needs() - for need in needs.values(): - # Data to use as filter context. - need_context: Dict[str, Any] = {**need} - need_context.update(**needs_config.filter_data) # Add needs_filter_data to filter context - _sphinx_tags = env.app.builder.tags.tags # Get sphinx tags - need_context.update(**_sphinx_tags) # Add sphinx tags to filter context - - for var_option in variants_options: - if var_option in need and need[var_option] not in (None, "", []): - if not isinstance(need[var_option], (list, set, tuple)): - option_value: str = need[var_option] - need[var_option] = match_variants(option_value, need_context, needs_config.variants) - else: - option_value = need[var_option] - need[var_option] = match_variants(option_value, need_context, needs_config.variants) + if not variants_options: + return - # Finally set a flag so that this function gets not executed several times - workflow["variant_option_resolved"] = True + needs = data.get_or_create_needs() + for need in needs.values(): + # Data to use as filter context. + need_context: Dict[str, Any] = {**need} + need_context.update(**needs_config.filter_data) # Add needs_filter_data to filter context + _sphinx_tags = env.app.builder.tags.tags # Get sphinx tags + need_context.update(**_sphinx_tags) # Add sphinx tags to filter context + + for var_option in variants_options: + if var_option in need and need[var_option] not in (None, "", []): + if not isinstance(need[var_option], (list, set, tuple)): + option_value: str = need[var_option] + need[var_option] = match_variants(option_value, need_context, needs_config.variants) + else: + option_value = need[var_option] + need[var_option] = match_variants(option_value, need_context, needs_config.variants) def check_and_get_content(content: str, need, env: BuildEnvironment) -> str: diff --git a/sphinx_needs/need_constraints.py b/sphinx_needs/need_constraints.py index 910526f22..52db77436 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -13,19 +13,17 @@ def process_constraints(app: Sphinx) -> None: - """Analyse constraints of a single need, - and set corresponding fields on the need data item. + """Analyse constraints of all needs, + and set corresponding fields on the need data item: + ``constraints_passed`` and ``constraints_results``. + + The ``style`` field may also be changed, if a constraint fails + (depending on the config value ``constraint_failed_options``) """ env = app.env needs_config = NeedsSphinxConfig(env.config) config_constraints = needs_config.constraints needs = SphinxNeedsData(env).get_or_create_needs() - workflow = SphinxNeedsData(env).get_or_create_workflow() - - if workflow["needs_constraints"]: - return - - workflow["needs_constraints"] = True error_templates_cache: Dict[str, jinja2.Template] = {} diff --git a/sphinx_needs/needs.py b/sphinx_needs/needs.py index fd61244ac..13b73d4d7 100644 --- a/sphinx_needs/needs.py +++ b/sphinx_needs/needs.py @@ -489,8 +489,6 @@ def prepare_env(app: Sphinx, env: BuildEnvironment, _docname: str) -> None: needs_config.flow_configs.update(NEEDFLOW_CONFIG_DEFAULTS) - data.get_or_create_workflow() - # Set time measurement flag if needs_config.debug_measurement: debug.START_TIME = timer() # Store the rough start time of Sphinx build From ad00acc8e90720f8595adac005f6d85d2f437cc1 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 29 Sep 2023 08:45:25 -0300 Subject: [PATCH 11/14] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20needs=20p?= =?UTF-8?q?ost-processing=20function=20signatures=20(#1040)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit refactors the functions that post-process needs data, to only get passed what they actually require (rather than just `app` or `env`). This makes it clearer what they are doing, and in all cases except `resolve_dynamic_values` effectively de-couples the post-processing from Sphinx, since you just need to pass them the needs data and needs configuration. --- sphinx_needs/api/need.py | 3 +- sphinx_needs/builder.py | 6 ++- sphinx_needs/config.py | 2 + sphinx_needs/directives/need.py | 35 +++++--------- sphinx_needs/directives/needbar.py | 2 +- sphinx_needs/directives/needextend.py | 16 +++---- sphinx_needs/directives/needflow.py | 4 +- sphinx_needs/directives/needgantt.py | 4 +- sphinx_needs/directives/needimport.py | 5 +- sphinx_needs/directives/needpie.py | 5 +- sphinx_needs/directives/needsequence.py | 4 +- sphinx_needs/directives/needuml.py | 3 +- sphinx_needs/filter_common.py | 25 +++++----- sphinx_needs/functions/common.py | 12 +++-- sphinx_needs/functions/functions.py | 64 ++++++++++++++----------- sphinx_needs/need_constraints.py | 16 +++---- sphinx_needs/roles/need_count.py | 12 ++--- sphinx_needs/warnings.py | 10 ++-- 18 files changed, 117 insertions(+), 111 deletions(-) diff --git a/sphinx_needs/api/need.py b/sphinx_needs/api/need.py index f3415cc5b..5ae06f619 100644 --- a/sphinx_needs/api/need.py +++ b/sphinx_needs/api/need.py @@ -746,6 +746,7 @@ def _merge_global_options(app: Sphinx, needs_info, global_options) -> None: """Add all global defined options to needs_info""" if global_options is None: return + config = NeedsSphinxConfig(app.config) for key, value in global_options.items(): # If key already exists in needs_info, this global_option got overwritten manually in current need if key in needs_info and needs_info[key]: @@ -762,7 +763,7 @@ def _merge_global_options(app: Sphinx, needs_info, global_options) -> None: for single_value in values: if len(single_value) < 2 or len(single_value) > 3: raise NeedsInvalidException(f"global option tuple has wrong amount of parameters: {key}") - if filter_single_need(app, needs_info, single_value[1]): + if filter_single_need(needs_info, config, single_value[1]): # Set value, if filter has matched needs_info[key] = single_value[0] elif len(single_value) == 3 and (key not in needs_info.keys() or len(str(needs_info[key])) > 0): diff --git a/sphinx_needs/builder.py b/sphinx_needs/builder.py index 56bed77cc..454cc95b9 100644 --- a/sphinx_needs/builder.py +++ b/sphinx_needs/builder.py @@ -48,7 +48,9 @@ def finish(self) -> None: from sphinx_needs.filter_common import filter_needs filter_string = needs_config.builder_filter - filtered_needs: List[NeedsInfoType] = filter_needs(self.app, data.get_or_create_needs().values(), filter_string) + filtered_needs: List[NeedsInfoType] = filter_needs( + data.get_or_create_needs().values(), needs_config, filter_string + ) for need in filtered_needs: needs_list.add_need(version, need) @@ -181,7 +183,7 @@ def finish(self) -> None: filter_string = needs_config.builder_filter from sphinx_needs.filter_common import filter_needs - filtered_needs = filter_needs(self.app, needs, filter_string) + filtered_needs = filter_needs(needs, needs_config, filter_string) needs_build_json_per_id_path = needs_config.build_json_per_id_path needs_dir = os.path.join(self.outdir, needs_build_json_per_id_path) if not os.path.exists(needs_dir): diff --git a/sphinx_needs/config.py b/sphinx_needs/config.py index da452c424..0ba6c559f 100644 --- a/sphinx_needs/config.py +++ b/sphinx_needs/config.py @@ -87,6 +87,8 @@ def __init__(self, config: _SphinxConfig) -> None: super().__setattr__("_config", config) def __getattribute__(self, name: str) -> Any: + if name.startswith("__"): + return super().__getattribute__(name) return getattr(super().__getattribute__("_config"), f"needs_{name}") def __setattr__(self, name: str, value: Any) -> None: diff --git a/sphinx_needs/directives/need.py b/sphinx_needs/directives/need.py index 3895bdf5a..2636f403a 100644 --- a/sphinx_needs/directives/need.py +++ b/sphinx_needs/directives/need.py @@ -14,7 +14,7 @@ from sphinx_needs.api import add_need from sphinx_needs.api.exceptions import NeedsInvalidException from sphinx_needs.config import NEEDS_CONFIG, NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.debug import measure_time from sphinx_needs.defaults import NEED_DEFAULT_OPTIONS from sphinx_needs.directives.needextend import ( @@ -366,11 +366,6 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - """ Event handler to add title meta data (status, tags, links, ...) information to the Need node. Also processes constraints. - - :param app: - :param doctree: - :param fromdocname: - :return: """ needs_config = NeedsSphinxConfig(app.config) if not needs_config.include_needs: @@ -381,18 +376,19 @@ def process_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str) - env = app.env needs_data = SphinxNeedsData(env) + needs = needs_data.get_or_create_needs() # If no needs were defined, we do not need to do anything - if not needs_data.get_or_create_needs(): + if not needs: return if not needs_data.needs_is_post_processed: - resolve_dynamic_values(env) - resolve_variants_options(env) - check_links(env) - create_back_links(env) - process_constraints(app) - extend_needs_data(app) + resolve_dynamic_values(needs, app) + resolve_variants_options(needs, needs_config, app.builder.tags.tags) + check_links(needs, needs_config) + create_back_links(needs, needs_config) + process_constraints(needs, needs_config) + extend_needs_data(needs, needs_data.get_or_create_extends(), needs_config) needs_data.needs_is_post_processed = True for extend_node in doctree.findall(Needextend): @@ -429,16 +425,13 @@ def print_need_nodes(app: Sphinx, doctree: nodes.document, fromdocname: str, fou build_need(layout, node_need, app, fromdocname=fromdocname) -def check_links(env: BuildEnvironment) -> None: +def check_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Checks if set links are valid or are dead (referenced need does not exist.) For needs with dead links, an extra ``has_dead_links`` field is added and, if the link is not allowed to be dead, the ``has_forbidden_dead_links`` field is also added. """ - config = NeedsSphinxConfig(env.config) - data = SphinxNeedsData(env) - needs = data.get_or_create_needs() extra_links = config.extra_links for need in needs.values(): for link_type in extra_links: @@ -461,17 +454,13 @@ def check_links(env: BuildEnvironment) -> None: break # One found dead link is enough -def create_back_links(env: BuildEnvironment) -> None: +def create_back_links(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Create back-links in all found needs. These are fields for each link type, ``_back``, which contain a list of all IDs of needs that link to the current need. """ - data = SphinxNeedsData(env) - needs_config = NeedsSphinxConfig(env.config) - needs = data.get_or_create_needs() - - for links in needs_config.extra_links: + for links in config.extra_links: option = links["option"] option_back = f"{option}_back" diff --git a/sphinx_needs/directives/needbar.py b/sphinx_needs/directives/needbar.py index a61daab6a..1ddd5d572 100644 --- a/sphinx_needs/directives/needbar.py +++ b/sphinx_needs/directives/needbar.py @@ -271,7 +271,7 @@ def process_needbar(app: Sphinx, doctree: nodes.document, fromdocname: str, foun if element.isdigit(): line_number.append(float(element)) else: - result = len(filter_needs(app, need_list, element)) + result = len(filter_needs(need_list, needs_config, element)) line_number.append(float(result)) local_data_number.append(line_number) diff --git a/sphinx_needs/directives/needextend.py b/sphinx_needs/directives/needextend.py index 278593864..fdfc3789a 100644 --- a/sphinx_needs/directives/needextend.py +++ b/sphinx_needs/directives/needextend.py @@ -7,12 +7,11 @@ from docutils import nodes from docutils.parsers.rst import directives -from sphinx.application import Sphinx from sphinx.util.docutils import SphinxDirective from sphinx_needs.api.exceptions import NeedsInvalidFilter from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsExtendType, NeedsInfoType, SphinxNeedsData from sphinx_needs.filter_common import filter_needs from sphinx_needs.logging import get_logger from sphinx_needs.utils import add_doc @@ -71,11 +70,10 @@ def run(self) -> Sequence[nodes.Node]: return [targetnode, Needextend("")] -def extend_needs_data(app: Sphinx) -> None: +def extend_needs_data( + all_needs: Dict[str, NeedsInfoType], extends: Dict[str, NeedsExtendType], needs_config: NeedsSphinxConfig +) -> None: """Use data gathered from needextend directives to modify fields of existing needs.""" - env = app.env - needs_config = NeedsSphinxConfig(env.config) - data = SphinxNeedsData(env) list_values = ( ["tags", "links"] @@ -84,9 +82,7 @@ def extend_needs_data(app: Sphinx) -> None: ) # back-links (incoming) link_names = [x["option"] for x in needs_config.extra_links] - all_needs = data.get_or_create_needs() - - for current_needextend in data.get_or_create_extends().values(): + for current_needextend in extends.values(): need_filter = current_needextend["filter"] if need_filter in all_needs: # a single known ID @@ -102,7 +98,7 @@ def extend_needs_data(app: Sphinx) -> None: else: # a filter string try: - found_needs = filter_needs(app, all_needs.values(), need_filter) + found_needs = filter_needs(all_needs.values(), needs_config, need_filter) except NeedsInvalidFilter as e: raise NeedsInvalidFilter( f"Filter not valid for needextend on page {current_needextend['docname']}:\n{e}" diff --git a/sphinx_needs/directives/needflow.py b/sphinx_needs/directives/needflow.py index 98ead1bd6..e586b4d70 100644 --- a/sphinx_needs/directives/needflow.py +++ b/sphinx_needs/directives/needflow.py @@ -130,7 +130,9 @@ def get_need_node_rep_for_plantuml( # We set # later, as the user may not have given a color and the node must get highlighted node_colors.append(need_info["type_color"].replace("#", "")) - if current_needflow["highlight"] and filter_single_need(app, need_info, current_needflow["highlight"], all_needs): + if current_needflow["highlight"] and filter_single_need( + need_info, needs_config, current_needflow["highlight"], all_needs + ): node_colors.append("line:FF0000") # need parts style use default "rectangle" diff --git a/sphinx_needs/directives/needgantt.py b/sphinx_needs/directives/needgantt.py index abec283bd..ec6dc182d 100644 --- a/sphinx_needs/directives/needgantt.py +++ b/sphinx_needs/directives/needgantt.py @@ -214,7 +214,7 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo complete = None if current_needgantt["milestone_filter"]: - is_milestone = filter_single_need(app, need, current_needgantt["milestone_filter"]) + is_milestone = filter_single_need(need, needs_config, current_needgantt["milestone_filter"]) else: is_milestone = False @@ -259,7 +259,7 @@ def process_needgantt(app: Sphinx, doctree: nodes.document, fromdocname: str, fo puml_node["uml"] += "\n' Constraints definition \n\n" for need in found_needs: if current_needgantt["milestone_filter"]: - is_milestone = filter_single_need(app, need, current_needgantt["milestone_filter"]) + is_milestone = filter_single_need(need, needs_config, current_needgantt["milestone_filter"]) else: is_milestone = False for con_type in ("starts_with_links", "starts_after_links", "ends_with_links"): diff --git a/sphinx_needs/directives/needimport.py b/sphinx_needs/directives/needimport.py index cdccd1409..299d718d4 100644 --- a/sphinx_needs/directives/needimport.py +++ b/sphinx_needs/directives/needimport.py @@ -123,6 +123,7 @@ def run(self) -> Sequence[nodes.Node]: if version not in needs_import_list["versions"].keys(): raise VersionNotFound(f"Version {version} not found in needs import file {correct_need_import_path}") + needs_config = NeedsSphinxConfig(self.config) # TODO this is not exactly NeedsInfoType, because the export removes/adds some keys needs_list: Dict[str, NeedsInfoType] = needs_import_list["versions"][version]["needs"] @@ -138,7 +139,7 @@ def run(self) -> Sequence[nodes.Node]: # "content" is the sphinx internal name for this kind of information filter_context["content"] = need["description"] # type: ignore[typeddict-item] try: - if filter_single_need(self.env.app, filter_context, filter_string): + if filter_single_need(filter_context, needs_config, filter_string): needs_list_filtered[key] = need except Exception as e: logger.warning( @@ -152,7 +153,7 @@ def run(self) -> Sequence[nodes.Node]: needs_list = needs_list_filtered # If we need to set an id prefix, we also need to manipulate all used ids in the imported data. - extra_links = NeedsSphinxConfig(self.config).extra_links + extra_links = needs_config.extra_links if id_prefix: for need in needs_list.values(): for id in needs_list: diff --git a/sphinx_needs/directives/needpie.py b/sphinx_needs/directives/needpie.py index 5b2659f8e..09bc3c70c 100644 --- a/sphinx_needs/directives/needpie.py +++ b/sphinx_needs/directives/needpie.py @@ -111,9 +111,10 @@ def run(self) -> Sequence[nodes.Node]: def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, found_nodes: List[nodes.Element]) -> None: env = app.env needs_data = SphinxNeedsData(env) + needs_config = NeedsSphinxConfig(env.config) # NEEDFLOW - include_needs = NeedsSphinxConfig(env.config).include_needs + include_needs = needs_config.include_needs # for node in doctree.findall(Needpie): for node in found_nodes: if not include_needs: @@ -146,7 +147,7 @@ def process_needpie(app: Sphinx, doctree: nodes.document, fromdocname: str, foun if line.isdigit(): sizes.append(abs(float(line))) else: - result = len(filter_needs(app, need_list, line)) + result = len(filter_needs(need_list, needs_config, line)) sizes.append(result) elif current_needpie["filter_func"] and not content: try: diff --git a/sphinx_needs/directives/needsequence.py b/sphinx_needs/directives/needsequence.py index c250921df..63ccd3aad 100644 --- a/sphinx_needs/directives/needsequence.py +++ b/sphinx_needs/directives/needsequence.py @@ -259,7 +259,9 @@ def get_message_needs( if filter: from sphinx_needs.filter_common import filter_single_need - if not filter_single_need(app, all_needs_dict[rec_id], filter, needs=all_needs_dict.values()): + if not filter_single_need( + all_needs_dict[rec_id], NeedsSphinxConfig(app.config), filter, needs=all_needs_dict.values() + ): continue rec_data = {"id": rec_id, "title": all_needs_dict[rec_id]["title"], "messages": []} diff --git a/sphinx_needs/directives/needuml.py b/sphinx_needs/directives/needuml.py index e3ea4365b..bd679cdbb 100644 --- a/sphinx_needs/directives/needuml.py +++ b/sphinx_needs/directives/needuml.py @@ -350,8 +350,9 @@ def filter(self, filter_string): """ Return a list of found needs that pass the given filter string. """ + needs_config = NeedsSphinxConfig(self.app.config) - return filter_needs(self.app, list(self.needs.values()), filter_string=filter_string) + return filter_needs(list(self.needs.values()), needs_config, filter_string=filter_string) def imports(self, *args): if not self.parent_need_id: diff --git a/sphinx_needs/filter_common.py b/sphinx_needs/filter_common.py index 5d68dff6c..e5e9bed04 100644 --- a/sphinx_needs/filter_common.py +++ b/sphinx_needs/filter_common.py @@ -102,6 +102,7 @@ def process_filters( :return: list of needs, which passed the filters """ + needs_config = NeedsSphinxConfig(app.config) found_needs: list[NeedsPartsInfoType] sort_key = filter_data["sort_by"] if sort_key: @@ -156,13 +157,13 @@ def process_filters( if status_filter_passed and tags_filter_passed and type_filter_passed: found_needs_by_options.append(need_info) # Get need by filter string - found_needs_by_string = filter_needs(app, all_needs_incl_parts, filter_data["filter"]) + found_needs_by_string = filter_needs(all_needs_incl_parts, needs_config, filter_data["filter"]) # Make an intersection of both lists found_needs = intersection_of_need_results(found_needs_by_options, found_needs_by_string) else: # There is no other config as the one for filter string. # So we only need this result. - found_needs = filter_needs(app, all_needs_incl_parts, filter_data["filter"]) + found_needs = filter_needs(all_needs_incl_parts, needs_config, filter_data["filter"]) else: # Provides only a copy of needs to avoid data manipulations. context = { @@ -192,7 +193,7 @@ def process_filters( found_needs = [] # Check if config allow unsafe filters - if NeedsSphinxConfig(app.config).allow_unsafe_filters: + if needs_config.allow_unsafe_filters: found_needs = found_dirty_needs else: # Just take the ids from search result and use the related, but original need @@ -203,8 +204,7 @@ def process_filters( # Store basic filter configuration and result global list. # Needed mainly for exporting the result to needs.json (if builder "needs" is used). - env = app.env - filter_list = SphinxNeedsData(env).get_or_create_filters() + filter_list = SphinxNeedsData(app.env).get_or_create_filters() found_needs_ids = [need["id_complete"] for need in found_needs] filter_list[filter_data["target_id"]] = { @@ -258,8 +258,8 @@ def intersection_of_need_results(list_a: list[T], list_b: list[T]) -> list[T]: @measure_time("filtering") def filter_needs( - app: Sphinx, needs: Iterable[V], + config: NeedsSphinxConfig, filter_string: None | str = "", current_need: NeedsInfoType | None = None, ) -> list[V]: @@ -267,14 +267,13 @@ def filter_needs( Filters given needs based on a given filter string. Returns all needs, which pass the given filter. - :param app: Sphinx application object :param needs: list of needs, which shall be filtered + :param config: NeedsSphinxConfig object :param filter_string: strings, which gets evaluated against each need :param current_need: current need, which uses the filter. :return: list of found needs """ - if not filter_string: return list(needs) @@ -286,7 +285,7 @@ def filter_needs( for filter_need in needs: try: if filter_single_need( - app, filter_need, filter_string, needs, current_need, filter_compiled=filter_compiled + filter_need, config, filter_string, needs, current_need, filter_compiled=filter_compiled ): found_needs.append(filter_need) except Exception as e: @@ -300,8 +299,8 @@ def filter_needs( @measure_time("filtering") def filter_single_need( - app: Sphinx, need: NeedsInfoType, + config: NeedsSphinxConfig, filter_string: str = "", needs: Iterable[NeedsInfoType] | None = None, current_need: NeedsInfoType | None = None, @@ -310,8 +309,8 @@ def filter_single_need( """ Checks if a single need/need_part passes a filter_string - :param app: Sphinx application object - :param current_need: + :param need: the data for a single need + :param config: NeedsSphinxConfig object :param filter_compiled: An already compiled filter_string to safe time :param need: need or need_part :param filter_string: string, which is used as input for eval() @@ -327,7 +326,7 @@ def filter_single_need( filter_context["current_need"] = need # Get needs external filter data and merge to filter_context - filter_context.update(NeedsSphinxConfig(app.config).filter_data) + filter_context.update(config.filter_data) filter_context["search"] = re.search result = False diff --git a/sphinx_needs/functions/common.py b/sphinx_needs/functions/common.py index 29638c123..69d6f4e37 100644 --- a/sphinx_needs/functions/common.py +++ b/sphinx_needs/functions/common.py @@ -11,6 +11,7 @@ from sphinx.application import Sphinx from sphinx_needs.api.exceptions import NeedsInvalidFilter +from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import NeedsInfoType from sphinx_needs.filter_common import filter_needs, filter_single_need from sphinx_needs.utils import logger @@ -151,7 +152,7 @@ def copy( need = needs[need_id] if filter: - result = filter_needs(app, needs.values(), filter, need) + result = filter_needs(needs.values(), NeedsSphinxConfig(app.config), filter, need) if result: need = result[0] @@ -308,6 +309,7 @@ def check_linked_values( :param one_hit: If True, only one linked need must have a positive check :return: result, if all checks are positive """ + needs_config = NeedsSphinxConfig(app.config) links = need["links"] if not isinstance(search_value, list): search_value = [search_value] @@ -316,7 +318,7 @@ def check_linked_values( need = needs[link] if filter_string: try: - if not filter_single_need(app, need, filter_string): + if not filter_single_need(need, needs_config, filter_string): continue except Exception as e: logger.warning(f"CheckLinkedValues: Filter {filter_string} not valid: Error: {e} [needs]", type="needs") @@ -417,6 +419,7 @@ def calc_sum( :return: A float number """ + needs_config = NeedsSphinxConfig(app.config) check_needs = [needs[link] for link in need["links"]] if links_only else needs.values() calculated_sum = 0.0 @@ -424,7 +427,7 @@ def calc_sum( for check_need in check_needs: if filter: try: - if not filter_single_need(app, check_need, filter): + if not filter_single_need(check_need, needs_config, filter): continue except ValueError: pass @@ -506,9 +509,10 @@ def links_from_content( raw_links.append(link[1]) if filter: + needs_config = NeedsSphinxConfig(app.config) filtered_links = [] for link in raw_links: - if link not in filtered_links and filter_single_need(app, needs[link], filter): + if link not in filtered_links and filter_single_need(needs[link], needs_config, filter): filtered_links.append(link) return filtered_links diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index ab17613bc..97c7334a2 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -16,7 +16,7 @@ from sphinx.errors import SphinxError from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsInfoType, SphinxNeedsData from sphinx_needs.debug import measure_time_func from sphinx_needs.logging import get_logger from sphinx_needs.utils import NEEDS_FUNCTIONS, match_variants # noqa: F401 @@ -25,8 +25,10 @@ unicode = str ast_boolean = ast.NameConstant -# TODO this input args should actually be of type `Need` and `List[Need]`, however `Need` is *currently* untyped. -DynamicFunction = Callable[[Sphinx, Any, Any], Union[str, int, float, List[Union[str, int, float]]]] +# TODO these functions also take optional *args and **kwargs +DynamicFunction = Callable[ + [Sphinx, NeedsInfoType, Dict[str, NeedsInfoType]], Union[str, int, float, List[Union[str, int, float]]] +] def register_func(need_function: DynamicFunction, name: Optional[str] = None) -> None: @@ -56,12 +58,12 @@ def register_func(need_function: DynamicFunction, name: Optional[str] = None) -> NEEDS_FUNCTIONS[func_name] = {"name": func_name, "function": need_function} -def execute_func(env: BuildEnvironment, need, func_string: str): - """ - Executes a given function string. +def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str): + """Executes a given function string. + :param env: Sphinx environment :param need: Actual need, which contains the found function string - :param func_string: string of the found function. Without [[ ]] + :param func_string: string of the found function. Without ``[[ ]]`` :return: return value of executed function """ global NEEDS_FUNCTIONS @@ -71,7 +73,7 @@ def execute_func(env: BuildEnvironment, need, func_string: str): raise SphinxError("Unknown dynamic sphinx-needs function: {}. Found in need: {}".format(func_name, need["id"])) func = measure_time_func(NEEDS_FUNCTIONS[func_name]["function"], category="dyn_func", source="user") - func_return = func(env.app, need, SphinxNeedsData(env).get_or_create_needs(), *func_args, **func_kwargs) + func_return = func(app, need, SphinxNeedsData(app.env).get_or_create_needs(), *func_args, **func_kwargs) if not isinstance(func_return, (str, int, float, list, unicode)) and func_return: raise SphinxError( @@ -93,7 +95,7 @@ def execute_func(env: BuildEnvironment, need, func_string: str): func_pattern = re.compile(r"\[\[(.*?)\]\]") # RegEx to detect function strings -def find_and_replace_node_content(node, env: BuildEnvironment, need): +def find_and_replace_node_content(node, env: BuildEnvironment, need: NeedsInfoType): """ Search inside a given node and its children for nodes of type Text, if found, check if it contains a function string and run/replace it. @@ -125,7 +127,7 @@ def find_and_replace_node_content(node, env: BuildEnvironment, need): func_string = func_string.replace("‘", "'") func_string = func_string.replace("’", "'") - func_return = execute_func(env, need, func_string) + func_return = execute_func(env.app, need, func_string) # This should never happen, but we can not be sure. if isinstance(func_return, list): @@ -151,20 +153,25 @@ def find_and_replace_node_content(node, env: BuildEnvironment, need): return node -def resolve_dynamic_values(env: BuildEnvironment) -> None: +def resolve_dynamic_values(needs: Dict[str, NeedsInfoType], app: Sphinx) -> None: """ Resolve dynamic values inside need data. Rough workflow: - #. Parse all needs and their data for a string like ``[[ my_func(a,b,c) ]]`` + #. Parse all needs and their field values for a string like ``[[ my_func(a, b, c) ]]`` #. Extract function name and call parameters #. Execute registered function name with extracted call parameters #. Replace original string with return value - """ - data = SphinxNeedsData(env) - needs = data.get_or_create_needs() + The registered functions should take the following parameters: + + - ``app``: Sphinx application + - ``need``: Need data + - ``all_needs``: All needs of the current sphinx project + - ``*args``: optional arguments (specified in the function string) + - ``**kwargs``: optional keyword arguments (specified in the function string) + """ for need in needs.values(): for need_option in need: if need_option in ["docname", "lineno", "content", "content_node", "content_id"]: @@ -174,7 +181,7 @@ def resolve_dynamic_values(env: BuildEnvironment) -> None: func_call = True while func_call: try: - func_call, func_return = _detect_and_execute(need[need_option], need, env) + func_call, func_return = _detect_and_execute(need[need_option], need, app) except FunctionParsingException: raise SphinxError( "Function definition of {option} in file {file}:{line} has " @@ -198,7 +205,7 @@ def resolve_dynamic_values(env: BuildEnvironment) -> None: new_values = [] for element in need[need_option]: try: - func_call, func_return = _detect_and_execute(element, need, env) + func_call, func_return = _detect_and_execute(element, need, app) except FunctionParsingException: raise SphinxError( "Function definition of {option} in file {file}:{line} has " @@ -223,29 +230,31 @@ def resolve_dynamic_values(env: BuildEnvironment) -> None: need[need_option] = new_values -def resolve_variants_options(env: BuildEnvironment) -> None: +def resolve_variants_options( + needs: Dict[str, NeedsInfoType], needs_config: NeedsSphinxConfig, tags: Dict[str, bool] +) -> None: """ Resolve variants options inside need data. + These are fields specified by the user, + that have string values with a special markup syntax like ``var_a:open``. + These need to be resolved to the actual value. + Rough workflow: - #. Parse all needs and their data for variant handling + #. Parse all needs and their fields for variant handling #. Replace original string with return value """ - data = SphinxNeedsData(env) - needs_config = NeedsSphinxConfig(env.config) variants_options = needs_config.variant_options if not variants_options: return - needs = data.get_or_create_needs() for need in needs.values(): # Data to use as filter context. need_context: Dict[str, Any] = {**need} need_context.update(**needs_config.filter_data) # Add needs_filter_data to filter context - _sphinx_tags = env.app.builder.tags.tags # Get sphinx tags - need_context.update(**_sphinx_tags) # Add sphinx tags to filter context + need_context.update(**tags) # Add sphinx tags to filter context for var_option in variants_options: if var_option in need and need[var_option] not in (None, "", []): @@ -279,14 +288,15 @@ def check_and_get_content(content: str, need, env: BuildEnvironment) -> str: return content func_call = func_match.group(1) # Extract function call - func_return = execute_func(env, need, func_call) # Execute function call and get return value + func_return = execute_func(env.app, need, func_call) # Execute function call and get return value # Replace the function_call with the calculated value content = content.replace(f"[[{func_call}]]", func_return) return content -def _detect_and_execute(content, need, env): +def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx): + """Detects if given content is a function call and executes it.""" try: content = str(content) except UnicodeEncodeError: @@ -297,7 +307,7 @@ def _detect_and_execute(content, need, env): return None, None func_call = func_match.group(1) # Extract function call - func_return = execute_func(env, need, func_call) # Execute function call and get return value + func_return = execute_func(app, need, func_call) # Execute function call and get return value return func_call, func_return diff --git a/sphinx_needs/need_constraints.py b/sphinx_needs/need_constraints.py index 52db77436..77db7d03b 100644 --- a/sphinx_needs/need_constraints.py +++ b/sphinx_needs/need_constraints.py @@ -1,18 +1,17 @@ from typing import Dict import jinja2 -from sphinx.application import Sphinx from sphinx_needs.api.exceptions import NeedsConstraintFailed, NeedsConstraintNotAllowed from sphinx_needs.config import NeedsSphinxConfig -from sphinx_needs.data import SphinxNeedsData +from sphinx_needs.data import NeedsInfoType from sphinx_needs.filter_common import filter_single_need from sphinx_needs.logging import get_logger logger = get_logger(__name__) -def process_constraints(app: Sphinx) -> None: +def process_constraints(needs: Dict[str, NeedsInfoType], config: NeedsSphinxConfig) -> None: """Analyse constraints of all needs, and set corresponding fields on the need data item: ``constraints_passed`` and ``constraints_results``. @@ -20,10 +19,7 @@ def process_constraints(app: Sphinx) -> None: The ``style`` field may also be changed, if a constraint fails (depending on the config value ``constraint_failed_options``) """ - env = app.env - needs_config = NeedsSphinxConfig(env.config) - config_constraints = needs_config.constraints - needs = SphinxNeedsData(env).get_or_create_needs() + config_constraints = config.constraints error_templates_cache: Dict[str, jinja2.Template] = {} @@ -50,7 +46,7 @@ def process_constraints(app: Sphinx) -> None: continue # compile constraint and check if need fulfils it - constraint_passed = filter_single_need(app, need, cmd) + constraint_passed = filter_single_need(need, config, cmd) if constraint_passed: need["constraints_results"].setdefault(constraint, {})[name] = True @@ -68,11 +64,11 @@ def process_constraints(app: Sphinx) -> None: f"'severity' key not set for constraint {constraint!r} in config 'needs_constraints'" ) severity = executable_constraints["severity"] - if severity not in needs_config.constraint_failed_options: + if severity not in config.constraint_failed_options: raise NeedsConstraintFailed( f"Severity {severity!r} not set in config 'needs_constraint_failed_options'" ) - failed_options = needs_config.constraint_failed_options[severity] + failed_options = config.constraint_failed_options[severity] # log/except if needed if "warn" in failed_options.get("on_fail", []): diff --git a/sphinx_needs/roles/need_count.py b/sphinx_needs/roles/need_count.py index cc5336580..cfe996c7b 100644 --- a/sphinx_needs/roles/need_count.py +++ b/sphinx_needs/roles/need_count.py @@ -10,6 +10,7 @@ from sphinx.application import Sphinx from sphinx_needs.api.exceptions import NeedsInvalidFilter +from sphinx_needs.config import NeedsSphinxConfig from sphinx_needs.data import SphinxNeedsData from sphinx_needs.filter_common import filter_needs, prepare_need_list from sphinx_needs.logging import get_logger @@ -24,21 +25,20 @@ class NeedCount(nodes.Inline, nodes.Element): # type: ignore def process_need_count( app: Sphinx, doctree: nodes.document, _fromdocname: str, found_nodes: List[nodes.Element] ) -> None: - env = app.env - # for node_need_count in doctree.findall(NeedCount): + needs_config = NeedsSphinxConfig(app.config) for node_need_count in found_nodes: - all_needs = list(SphinxNeedsData(env).get_or_create_needs().values()) + all_needs = list(SphinxNeedsData(app.env).get_or_create_needs().values()) filter = node_need_count["reftarget"] if filter: filters = filter.split(" ? ") if len(filters) == 1: need_list = prepare_need_list(all_needs) # adds parts to need_list - amount = str(len(filter_needs(app, need_list, filters[0]))) + amount = str(len(filter_needs(need_list, needs_config, filters[0]))) elif len(filters) == 2: need_list = prepare_need_list(all_needs) # adds parts to need_list - amount_1 = len(filter_needs(app, need_list, filters[0])) - amount_2 = len(filter_needs(app, need_list, filters[1])) + amount_1 = len(filter_needs(need_list, needs_config, filters[0])) + amount_2 = len(filter_needs(need_list, needs_config, filters[1])) amount = f"{amount_1 / amount_2 * 100:2.1f}" elif len(filters) > 2: raise NeedsInvalidFilter( diff --git a/sphinx_needs/warnings.py b/sphinx_needs/warnings.py index 4e237cb60..8754551dc 100644 --- a/sphinx_needs/warnings.py +++ b/sphinx_needs/warnings.py @@ -32,8 +32,9 @@ def process_warnings(app: Sphinx, exception: Optional[Exception]) -> None: return env = app.env + needs = SphinxNeedsData(env).get_or_create_needs() # If no needs were defined, we do not need to do anything - if not hasattr(env, "needs_all_needs"): + if not needs: return # Check if warnings already got executed. @@ -44,15 +45,14 @@ def process_warnings(app: Sphinx, exception: Optional[Exception]) -> None: env.needs_warnings_executed = True # type: ignore[attr-defined] - needs = SphinxNeedsData(env).get_or_create_needs() - # Exclude external needs for warnings check checked_needs: Dict[str, NeedsInfoType] = {} for need_id, need in needs.items(): if not need["is_external"]: checked_needs[need_id] = need - warnings_always_warn = NeedsSphinxConfig(app.config).warnings_always_warn + needs_config = NeedsSphinxConfig(app.config) + warnings_always_warn = needs_config.warnings_always_warn with logging.pending_logging(): logger.info("\nChecking sphinx-needs warnings") @@ -60,7 +60,7 @@ def process_warnings(app: Sphinx, exception: Optional[Exception]) -> None: for warning_name, warning_filter in NEEDS_CONFIG.warnings.items(): if isinstance(warning_filter, str): # filter string used - result = filter_needs(app, checked_needs.values(), warning_filter) + result = filter_needs(checked_needs.values(), needs_config, warning_filter) elif callable(warning_filter): # custom defined filter code used from conf.py result = [] From 425bf237c6a0a0c70688da05837cecc2d844a403 Mon Sep 17 00:00:00 2001 From: Chris Sewell Date: Fri, 29 Sep 2023 08:56:30 -0300 Subject: [PATCH 12/14] =?UTF-8?q?=F0=9F=94=A7=20Add=20strict=20typing=20fo?= =?UTF-8?q?r=20`sphinx=5Fneeds.functions.functions`=20(#1042)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 ++-- sphinx_needs/functions/functions.py | 34 ++++++++++++++--------------- sphinx_needs/roles/need_func.py | 2 +- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index e29feb20c..9ed82e217 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -111,13 +111,13 @@ ignore_missing_imports = true module = [ 'sphinx_needs.api.need', 'sphinx_needs.directives.needuml', - 'sphinx_needs.functions.functions', ] ignore_errors = true [[tool.mypy.overrides]] module = [ - "sphinx_needs.directives.needextend" + "sphinx_needs.directives.needextend", + "sphinx_needs.functions.functions", ] # TODO dynamically overriding TypeDict keys is a bit tricky disable_error_code = "literal-required" diff --git a/sphinx_needs/functions/functions.py b/sphinx_needs/functions/functions.py index 97c7334a2..52a8bb441 100644 --- a/sphinx_needs/functions/functions.py +++ b/sphinx_needs/functions/functions.py @@ -8,7 +8,7 @@ import ast import re -from typing import Any, Callable, Dict, List, Optional, Union +from typing import Any, Callable, Dict, List, Optional, Tuple, Union from docutils import nodes from sphinx.application import Sphinx @@ -58,7 +58,7 @@ def register_func(need_function: DynamicFunction, name: Optional[str] = None) -> NEEDS_FUNCTIONS[func_name] = {"name": func_name, "function": need_function} -def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str): +def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str) -> Any: """Executes a given function string. :param env: Sphinx environment @@ -95,7 +95,7 @@ def execute_func(app: Sphinx, need: NeedsInfoType, func_string: str): func_pattern = re.compile(r"\[\[(.*?)\]\]") # RegEx to detect function strings -def find_and_replace_node_content(node, env: BuildEnvironment, need: NeedsInfoType): +def find_and_replace_node_content(node: nodes.Node, env: BuildEnvironment, need: NeedsInfoType) -> nodes.Node: """ Search inside a given node and its children for nodes of type Text, if found, check if it contains a function string and run/replace it. @@ -111,7 +111,7 @@ def find_and_replace_node_content(node, env: BuildEnvironment, need: NeedsInfoTy except KeyError: # If no refuri is set, we don't need to modify anything. # So stop here and return the untouched node. - return node + return node # type: ignore else: new_text = node func_match = func_pattern.findall(new_text) @@ -144,7 +144,7 @@ def find_and_replace_node_content(node, env: BuildEnvironment, need: NeedsInfoTy node.children = new_children else: node = nodes.Text(new_text) - return node + return node # type: ignore else: for child in node.children: new_child = find_and_replace_node_content(child, env, need) @@ -178,7 +178,7 @@ def resolve_dynamic_values(needs: Dict[str, NeedsInfoType], app: Sphinx) -> None # dynamic values in this data are not allowed. continue if not isinstance(need[need_option], (list, set)): - func_call = True + func_call: Optional[str] = "init" while func_call: try: func_call, func_return = _detect_and_execute(need[need_option], need, app) @@ -266,7 +266,7 @@ def resolve_variants_options( need[var_option] = match_variants(option_value, need_context, needs_config.variants) -def check_and_get_content(content: str, need, env: BuildEnvironment) -> str: +def check_and_get_content(content: str, need: NeedsInfoType, env: BuildEnvironment) -> str: """ Checks if the given content is a function call. If not, content is returned. @@ -281,7 +281,7 @@ def check_and_get_content(content: str, need, env: BuildEnvironment) -> str: try: content = str(content) except UnicodeEncodeError: - content = content.encode("utf-8") + content = content.encode("utf-8") # type: ignore func_match = func_pattern.search(content) if func_match is None: @@ -295,7 +295,7 @@ def check_and_get_content(content: str, need, env: BuildEnvironment) -> str: return content -def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx): +def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx) -> Tuple[Optional[str], Any]: """Detects if given content is a function call and executes it.""" try: content = str(content) @@ -312,7 +312,7 @@ def _detect_and_execute(content: Any, need: NeedsInfoType, app: Sphinx): return func_call, func_return -def _analyze_func_string(func_string: str, need): +def _analyze_func_string(func_string: str, need: Optional[NeedsInfoType]) -> Tuple[str, List[Any], Dict[str, Any]]: """ Analyze given function string and extract: @@ -328,22 +328,22 @@ def _analyze_func_string(func_string: str, need): try: func = ast.parse(func_string) except SyntaxError as e: - need_id = need["id"] or "UNKNOWN" + need_id = need["id"] if need else "UNKNOWN" raise SphinxError(f"Parsing function string failed for need {need_id}: {func_string}. {e}") try: - func_call = func.body[0].value + func_call = func.body[0].value # type: ignore func_name = func_call.func.id except AttributeError: raise SphinxError(f"Given dynamic function string is not a valid python call. Got: {func_string}") - func_args = [] + func_args: List[Any] = [] for arg in func_call.args: if isinstance(arg, ast.Num): func_args.append(arg.n) elif isinstance(arg, (ast.Str, ast.BoolOp)): - func_args.append(arg.s) + func_args.append(arg.s) # type: ignore elif isinstance(arg, ast.List): - arg_list = [] + arg_list: List[Any] = [] for element in arg.elts: if isinstance(element, ast.Num): arg_list.append(element.n) @@ -351,7 +351,7 @@ def _analyze_func_string(func_string: str, need): arg_list.append(element.s) func_args.append(arg_list) elif isinstance(arg, ast.Attribute): - if arg.value.id == "need" and need: + if arg.value.id == "need" and need: # type: ignore func_args.append(need[arg.attr]) else: raise FunctionParsingException("usage of need attribute not supported.") @@ -367,7 +367,7 @@ def _analyze_func_string(func_string: str, need): "Unsupported type found in function definition: {}. " "Supported are numbers, strings, bool and list".format(func_string) ) - func_kargs = {} + func_kargs: Dict[str, Any] = {} for keyword in func_call.keywords: kvalue = keyword.value kkey = keyword.arg diff --git a/sphinx_needs/roles/need_func.py b/sphinx_needs/roles/need_func.py index 7d730b1a9..32ae6a7c6 100644 --- a/sphinx_needs/roles/need_func.py +++ b/sphinx_needs/roles/need_func.py @@ -23,6 +23,6 @@ def process_need_func( env = app.env # for node_need_func in doctree.findall(NeedFunc): for node_need_func in found_nodes: - result = check_and_get_content(node_need_func.attributes["reftarget"], {"id": "need_func_dummy"}, env) + result = check_and_get_content(node_need_func.attributes["reftarget"], {"id": "need_func_dummy"}, env) # type: ignore new_node_func = nodes.Text(str(result)) node_need_func.replace_self(new_node_func) From 52f1fa59040b0754ad66cb3606c5e903a6f69d33 Mon Sep 17 00:00:00 2001 From: Randy Duodu Date: Mon, 23 Oct 2023 13:00:57 +0000 Subject: [PATCH 13/14] Updated conftest.py to enable JS testing using Cypress and PyTest. --- AUTHORS | 2 + Makefile | 6 +- docs/contributing.rst | 17 +- docs/requirements.txt | 3 +- poetry.lock | 1257 ++++++++++++++++----------- pyproject.toml | 3 +- sphinx_needs/layout.py | 8 +- tests/conftest.py | 39 +- tests/doc_test/doc_layout/conf.py | 7 + tests/doc_test/doc_layout/index.rst | 4 + tests/test_layouts.py | 5 +- 11 files changed, 836 insertions(+), 515 deletions(-) diff --git a/AUTHORS b/AUTHORS index 821726ea2..b37fbbb7c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -37,3 +37,5 @@ Duodu Randy Christian Wappler Chris Sewell + +Simon Leiner diff --git a/Makefile b/Makefile index 2261a8ac1..1eb38ffa0 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,11 @@ test: .PHONY: test test-short: - poetry run pytest -n auto --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + poetry run pytest -n auto -m "not jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ + +.PHONY: test +test-js: + poetry run pytest -n auto -m "jstest" --tb=long --ignore-glob="*official*" --ignore=tests/benchmarks tests/ .PHONY: benchmark-time benchmark-time: diff --git a/docs/contributing.rst b/docs/contributing.rst index f0f71cbbf..80aa42578 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -121,8 +121,9 @@ These snapshots can be updated by running: pip install -r docs/requirements.txt -Running JavaScript Tests in Python Test Files -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Running JS Testcases with PyTest +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + **Setup Cypress Locally** * Install Node JS on your computer and ensure it can be accessed through the CMD. @@ -133,7 +134,7 @@ Running JavaScript Tests in Python Test Files **Enable Cypress Test in Python Test Files** * Under the ``js_test`` folder, you can save your Cypress JS test files (files should end with: ``*.cy.js``). For each Cypress JS test file, you will need to write the Cypress JS test cases in the file. You can read more from the `Cypress Docs `_. You can also check the ``tests/js_test/sn-collapse-button.cy.js`` file as reference. -* In your Python test files, you must mark every JS related test case with the marker - ``jstest`` and you also need to pass the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter. For example, your test case could look like the below: +* In your Python test files, you must mark every JS related test case with the marker - ``jstest`` and you must also set the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter. For example, your test case could look like this: .. code-block:: python # tests/test_sn_collapse_button.py @@ -161,7 +162,7 @@ Running JavaScript Tests in Python Test Files The ``spec_pattern`` key is required to ensure Cypress locates your test files or folder. Visit this link for more info on how to set the `spec_pattern `_. -* After you have set the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter, you can call the ``app.test_js()`` in your Python test case to run the JS test for the ``spec_pattern`` you provided. For example, you can use it like below: +* After you set the ``spec_pattern`` key-value pair as part of the ``test_app`` fixture parameter, you can call ``app.test_js()`` in your Python test case to run a JS test for the ``spec_pattern`` you provided. For example, you can use ``app.test_js()`` like below: .. code-block:: python # tests/test_sn_collapse_button.py @@ -205,7 +206,13 @@ Running JavaScript Tests in Python Test Files "stdout": "Test passed string", "stderr": "Errors encountered, } - + +* You can run the ``make test-js`` command to check all JS testcases. + .. note:: + + The ``http_server`` process invoked by the ``make test-js`` command may not terminate properly in some instances. + Kindly check your system's monitoring app to end the process if not terminated automatically. + Linting & Formatting -------------------- diff --git a/docs/requirements.txt b/docs/requirements.txt index b0a0945f6..208889a87 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -7,5 +7,4 @@ sphinxcontrib-programoutput sphinx-design click tabulate -sphinx-immaterial==0.11.7 -pytest-xprocess \ No newline at end of file +sphinx-immaterial==0.11.7 \ No newline at end of file diff --git a/poetry.lock b/poetry.lock index 5ddfe8ac4..1c852fe8a 100644 --- a/poetry.lock +++ b/poetry.lock @@ -22,9 +22,6 @@ files = [ {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] @@ -34,18 +31,21 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte [[package]] name = "babel" -version = "2.12.1" +version = "2.13.0" description = "Internationalization utilities" optional = false python-versions = ">=3.7" files = [ - {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"}, - {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"}, + {file = "Babel-2.13.0-py3-none-any.whl", hash = "sha256:fbfcae1575ff78e26c7449136f1abbefc3c13ce542eeb13d43d50d8b047216ec"}, + {file = "Babel-2.13.0.tar.gz", hash = "sha256:04c3e2d28d2b7681644508f836be388ae49e0cfe91465095340395b60d00f210"}, ] [package.dependencies] pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + [[package]] name = "black" version = "22.12.0" @@ -73,7 +73,6 @@ mypy-extensions = ">=0.4.3" pathspec = ">=0.9.0" platformdirs = ">=2" tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} -typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] @@ -95,97 +94,112 @@ files = [ [[package]] name = "cfgv" -version = "3.3.1" +version = "3.4.0" description = "Validate configuration and produce human readable error messages." optional = false -python-versions = ">=3.6.1" +python-versions = ">=3.8" files = [ - {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, - {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, + {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, + {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, ] [[package]] name = "charset-normalizer" -version = "3.2.0" +version = "3.3.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, + {file = "charset-normalizer-3.3.1.tar.gz", hash = "sha256:d9137a876020661972ca6eec0766d81aef8a5627df628b664b234b73396e727e"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8aee051c89e13565c6bd366813c386939f8e928af93c29fda4af86d25b73d8f8"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:352a88c3df0d1fa886562384b86f9a9e27563d4704ee0e9d56ec6fcd270ea690"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:223b4d54561c01048f657fa6ce41461d5ad8ff128b9678cfe8b2ecd951e3f8a2"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f861d94c2a450b974b86093c6c027888627b8082f1299dfd5a4bae8e2292821"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1171ef1fc5ab4693c5d151ae0fdad7f7349920eabbaca6271f95969fa0756c2d"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28f512b9a33235545fbbdac6a330a510b63be278a50071a336afc1b78781b147"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0e842112fe3f1a4ffcf64b06dc4c61a88441c2f02f373367f7b4c1aa9be2ad5"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3f9bc2ce123637a60ebe819f9fccc614da1bcc05798bbbaf2dd4ec91f3e08846"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f194cce575e59ffe442c10a360182a986535fd90b57f7debfaa5c845c409ecc3"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9a74041ba0bfa9bc9b9bb2cd3238a6ab3b7618e759b41bd15b5f6ad958d17605"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b578cbe580e3b41ad17b1c428f382c814b32a6ce90f2d8e39e2e635d49e498d1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6db3cfb9b4fcecb4390db154e75b49578c87a3b9979b40cdf90d7e4b945656e1"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:debb633f3f7856f95ad957d9b9c781f8e2c6303ef21724ec94bea2ce2fcbd056"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win32.whl", hash = "sha256:87071618d3d8ec8b186d53cb6e66955ef2a0e4fa63ccd3709c0c90ac5a43520f"}, + {file = "charset_normalizer-3.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:e372d7dfd154009142631de2d316adad3cc1c36c32a38b16a4751ba78da2a397"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae4070f741f8d809075ef697877fd350ecf0b7c5837ed68738607ee0a2c572cf"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58e875eb7016fd014c0eea46c6fa92b87b62c0cb31b9feae25cbbe62c919f54d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dbd95e300367aa0827496fe75a1766d198d34385a58f97683fe6e07f89ca3e3c"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de0b4caa1c8a21394e8ce971997614a17648f94e1cd0640fbd6b4d14cab13a72"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:985c7965f62f6f32bf432e2681173db41336a9c2611693247069288bcb0c7f8b"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a15c1fe6d26e83fd2e5972425a772cca158eae58b05d4a25a4e474c221053e2d"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae55d592b02c4349525b6ed8f74c692509e5adffa842e582c0f861751701a673"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:be4d9c2770044a59715eb57c1144dedea7c5d5ae80c68fb9959515037cde2008"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:851cf693fb3aaef71031237cd68699dded198657ec1e76a76eb8be58c03a5d1f"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:31bbaba7218904d2eabecf4feec0d07469284e952a27400f23b6628439439fa7"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:871d045d6ccc181fd863a3cd66ee8e395523ebfbc57f85f91f035f50cee8e3d4"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:501adc5eb6cd5f40a6f77fbd90e5ab915c8fd6e8c614af2db5561e16c600d6f3"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f5fb672c396d826ca16a022ac04c9dce74e00a1c344f6ad1a0fdc1ba1f332213"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win32.whl", hash = "sha256:bb06098d019766ca16fc915ecaa455c1f1cd594204e7f840cd6258237b5079a8"}, + {file = "charset_normalizer-3.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:8af5a8917b8af42295e86b64903156b4f110a30dca5f3b5aedea123fbd638bff"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:7ae8e5142dcc7a49168f4055255dbcced01dc1714a90a21f87448dc8d90617d1"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5b70bab78accbc672f50e878a5b73ca692f45f5b5e25c8066d748c09405e6a55"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ceca5876032362ae73b83347be8b5dbd2d1faf3358deb38c9c88776779b2e2f"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34d95638ff3613849f473afc33f65c401a89f3b9528d0d213c7037c398a51296"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9edbe6a5bf8b56a4a84533ba2b2f489d0046e755c29616ef8830f9e7d9cf5728"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6a02a3c7950cafaadcd46a226ad9e12fc9744652cc69f9e5534f98b47f3bbcf"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10b8dd31e10f32410751b3430996f9807fc4d1587ca69772e2aa940a82ab571a"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:edc0202099ea1d82844316604e17d2b175044f9bcb6b398aab781eba957224bd"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b891a2f68e09c5ef989007fac11476ed33c5c9994449a4e2c3386529d703dc8b"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:71ef3b9be10070360f289aea4838c784f8b851be3ba58cf796262b57775c2f14"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:55602981b2dbf8184c098bc10287e8c245e351cd4fdcad050bd7199d5a8bf514"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:46fb9970aa5eeca547d7aa0de5d4b124a288b42eaefac677bde805013c95725c"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:520b7a142d2524f999447b3a0cf95115df81c4f33003c51a6ab637cbda9d0bf4"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win32.whl", hash = "sha256:8ec8ef42c6cd5856a7613dcd1eaf21e5573b2185263d87d27c8edcae33b62a61"}, + {file = "charset_normalizer-3.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:baec8148d6b8bd5cee1ae138ba658c71f5b03e0d69d5907703e3e1df96db5e41"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63a6f59e2d01310f754c270e4a257426fe5a591dc487f1983b3bbe793cf6bac6"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d6bfc32a68bc0933819cfdfe45f9abc3cae3877e1d90aac7259d57e6e0f85b1"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4f3100d86dcd03c03f7e9c3fdb23d92e32abbca07e7c13ebd7ddfbcb06f5991f"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:39b70a6f88eebe239fa775190796d55a33cfb6d36b9ffdd37843f7c4c1b5dc67"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e12f8ee80aa35e746230a2af83e81bd6b52daa92a8afaef4fea4a2ce9b9f4fa"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7b6cefa579e1237ce198619b76eaa148b71894fb0d6bcf9024460f9bf30fd228"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:61f1e3fb621f5420523abb71f5771a204b33c21d31e7d9d86881b2cffe92c47c"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4f6e2a839f83a6a76854d12dbebde50e4b1afa63e27761549d006fa53e9aa80e"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:1ec937546cad86d0dce5396748bf392bb7b62a9eeb8c66efac60e947697f0e58"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:82ca51ff0fc5b641a2d4e1cc8c5ff108699b7a56d7f3ad6f6da9dbb6f0145b48"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:633968254f8d421e70f91c6ebe71ed0ab140220469cf87a9857e21c16687c034"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win32.whl", hash = "sha256:c0c72d34e7de5604df0fde3644cc079feee5e55464967d10b24b1de268deceb9"}, + {file = "charset_normalizer-3.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:63accd11149c0f9a99e3bc095bbdb5a464862d77a7e309ad5938fbc8721235ae"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5a3580a4fdc4ac05f9e53c57f965e3594b2f99796231380adb2baaab96e22761"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2465aa50c9299d615d757c1c888bc6fef384b7c4aec81c05a0172b4400f98557"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb7cd68814308aade9d0c93c5bd2ade9f9441666f8ba5aa9c2d4b389cb5e2a45"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e43805ccafa0a91831f9cd5443aa34528c0c3f2cc48c4cb3d9a7721053874b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:854cc74367180beb327ab9d00f964f6d91da06450b0855cbbb09187bcdb02de5"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c15070ebf11b8b7fd1bfff7217e9324963c82dbdf6182ff7050519e350e7ad9f"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4c99f98fc3a1835af8179dcc9013f93594d0670e2fa80c83aa36346ee763d2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3fb765362688821404ad6cf86772fc54993ec11577cd5a92ac44b4c2ba52155b"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:dced27917823df984fe0c80a5c4ad75cf58df0fbfae890bc08004cd3888922a2"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a66bcdf19c1a523e41b8e9d53d0cedbfbac2e93c649a2e9502cb26c014d0980c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ecd26be9f112c4f96718290c10f4caea6cc798459a3a76636b817a0ed7874e42"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:3f70fd716855cd3b855316b226a1ac8bdb3caf4f7ea96edcccc6f484217c9597"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:17a866d61259c7de1bdadef418a37755050ddb4b922df8b356503234fff7932c"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win32.whl", hash = "sha256:548eefad783ed787b38cb6f9a574bd8664468cc76d1538215d510a3cd41406cb"}, + {file = "charset_normalizer-3.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:45f053a0ece92c734d874861ffe6e3cc92150e32136dd59ab1fb070575189c97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bc791ec3fd0c4309a753f95bb6c749ef0d8ea3aea91f07ee1cf06b7b02118f2f"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0c8c61fb505c7dad1d251c284e712d4e0372cef3b067f7ddf82a7fa82e1e9a93"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2c092be3885a1b7899cd85ce24acedc1034199d6fca1483fa2c3a35c86e43041"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c2000c54c395d9e5e44c99dc7c20a64dc371f777faf8bae4919ad3e99ce5253e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4cb50a0335382aac15c31b61d8531bc9bb657cfd848b1d7158009472189f3d62"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c30187840d36d0ba2893bc3271a36a517a717f9fd383a98e2697ee890a37c273"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe81b35c33772e56f4b6cf62cf4aedc1762ef7162a31e6ac7fe5e40d0149eb67"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d0bf89afcbcf4d1bb2652f6580e5e55a840fdf87384f6063c4a4f0c95e378656"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:06cf46bdff72f58645434d467bf5228080801298fbba19fe268a01b4534467f5"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3c66df3f41abee950d6638adc7eac4730a306b022570f71dd0bd6ba53503ab57"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd805513198304026bd379d1d516afbf6c3c13f4382134a2c526b8b854da1c2e"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:9505dc359edb6a330efcd2be825fdb73ee3e628d9010597aa1aee5aa63442e97"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:31445f38053476a0c4e6d12b047b08ced81e2c7c712e5a1ad97bc913256f91b2"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win32.whl", hash = "sha256:bd28b31730f0e982ace8663d108e01199098432a30a4c410d06fe08fdb9e93f4"}, + {file = "charset_normalizer-3.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:555fe186da0068d3354cdf4bbcbc609b0ecae4d04c921cc13e209eece7720727"}, + {file = "charset_normalizer-3.3.1-py3-none-any.whl", hash = "sha256:800561453acdecedaac137bf09cd719c7a440b6800ec182f077bb8e7025fb708"}, ] [[package]] @@ -201,7 +215,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -224,17 +237,150 @@ files = [ {file = "colored-1.4.4.tar.gz", hash = "sha256:04ff4d4dd514274fe3b99a21bb52fb96f2688c01e93fba7bef37221e7cb56ce0"}, ] +[[package]] +name = "contourpy" +version = "1.1.0" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89f06eff3ce2f4b3eb24c1055a26981bffe4e7264acd86f15b97e40530b794bc"}, + {file = "contourpy-1.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dffcc2ddec1782dd2f2ce1ef16f070861af4fb78c69862ce0aab801495dda6a3"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25ae46595e22f93592d39a7eac3d638cda552c3e1160255258b695f7b58e5655"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:17cfaf5ec9862bc93af1ec1f302457371c34e688fbd381f4035a06cd47324f48"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18a64814ae7bce73925131381603fff0116e2df25230dfc80d6d690aa6e20b37"}, + {file = "contourpy-1.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90c81f22b4f572f8a2110b0b741bb64e5a6427e0a198b2cdc1fbaf85f352a3aa"}, + {file = "contourpy-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:53cc3a40635abedbec7f1bde60f8c189c49e84ac180c665f2cd7c162cc454baa"}, + {file = "contourpy-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:1f795597073b09d631782e7245016a4323cf1cf0b4e06eef7ea6627e06a37ff2"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0b7b04ed0961647691cfe5d82115dd072af7ce8846d31a5fac6c142dcce8b882"}, + {file = "contourpy-1.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:27bc79200c742f9746d7dd51a734ee326a292d77e7d94c8af6e08d1e6c15d545"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:052cc634bf903c604ef1a00a5aa093c54f81a2612faedaa43295809ffdde885e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9382a1c0bc46230fb881c36229bfa23d8c303b889b788b939365578d762b5c18"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5cec36c5090e75a9ac9dbd0ff4a8cf7cecd60f1b6dc23a374c7d980a1cd710e"}, + {file = "contourpy-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f0cbd657e9bde94cd0e33aa7df94fb73c1ab7799378d3b3f902eb8eb2e04a3a"}, + {file = "contourpy-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:181cbace49874f4358e2929aaf7ba84006acb76694102e88dd15af861996c16e"}, + {file = "contourpy-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb3b7d9e6243bfa1efb93ccfe64ec610d85cfe5aec2c25f97fbbd2e58b531256"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bcb41692aa09aeb19c7c213411854402f29f6613845ad2453d30bf421fe68fed"}, + {file = "contourpy-1.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5d123a5bc63cd34c27ff9c7ac1cd978909e9c71da12e05be0231c608048bb2ae"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62013a2cf68abc80dadfd2307299bfa8f5aa0dcaec5b2954caeb5fa094171103"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0b6616375d7de55797d7a66ee7d087efe27f03d336c27cf1f32c02b8c1a5ac70"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:317267d915490d1e84577924bd61ba71bf8681a30e0d6c545f577363157e5e94"}, + {file = "contourpy-1.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d551f3a442655f3dcc1285723f9acd646ca5858834efeab4598d706206b09c9f"}, + {file = "contourpy-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e7a117ce7df5a938fe035cad481b0189049e8d92433b4b33aa7fc609344aafa1"}, + {file = "contourpy-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f26b25b4f86087e7d75e63212756c38546e70f2a92d2be44f80114826e1cd4"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bc00bb4225d57bff7ebb634646c0ee2a1298402ec10a5fe7af79df9a51c1bfd9"}, + {file = "contourpy-1.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:189ceb1525eb0655ab8487a9a9c41f42a73ba52d6789754788d1883fb06b2d8a"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f2931ed4741f98f74b410b16e5213f71dcccee67518970c42f64153ea9313b9"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30f511c05fab7f12e0b1b7730ebdc2ec8deedcfb505bc27eb570ff47c51a8f15"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:143dde50520a9f90e4a2703f367cf8ec96a73042b72e68fcd184e1279962eb6f"}, + {file = "contourpy-1.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e94bef2580e25b5fdb183bf98a2faa2adc5b638736b2c0a4da98691da641316a"}, + {file = "contourpy-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ed614aea8462735e7d70141374bd7650afd1c3f3cb0c2dbbcbe44e14331bf002"}, + {file = "contourpy-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:438ba416d02f82b692e371858143970ed2eb6337d9cdbbede0d8ad9f3d7dd17d"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a698c6a7a432789e587168573a864a7ea374c6be8d4f31f9d87c001d5a843493"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:397b0ac8a12880412da3551a8cb5a187d3298a72802b45a3bd1805e204ad8439"}, + {file = "contourpy-1.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:a67259c2b493b00e5a4d0f7bfae51fb4b3371395e47d079a4446e9b0f4d70e76"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2b836d22bd2c7bb2700348e4521b25e077255ebb6ab68e351ab5aa91ca27e027"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:084eaa568400cfaf7179b847ac871582199b1b44d5699198e9602ecbbb5f6104"}, + {file = "contourpy-1.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:911ff4fd53e26b019f898f32db0d4956c9d227d51338fb3b03ec72ff0084ee5f"}, + {file = "contourpy-1.1.0.tar.gz", hash = "sha256:e53046c3863828d21d531cc3b53786e6580eb1ba02477e8681009b6aa0870b21"}, +] + +[package.dependencies] +numpy = ">=1.16" + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.2.0)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] + +[[package]] +name = "contourpy" +version = "1.1.1" +description = "Python library for calculating contours of 2D quadrilateral grids" +optional = false +python-versions = ">=3.8" +files = [ + {file = "contourpy-1.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:46e24f5412c948d81736509377e255f6040e94216bf1a9b5ea1eaa9d29f6ec1b"}, + {file = "contourpy-1.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e48694d6a9c5a26ee85b10130c77a011a4fedf50a7279fa0bdaf44bafb4299d"}, + {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66045af6cf00e19d02191ab578a50cb93b2028c3eefed999793698e9ea768ae"}, + {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4ebf42695f75ee1a952f98ce9775c873e4971732a87334b099dde90b6af6a916"}, + {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f6aec19457617ef468ff091669cca01fa7ea557b12b59a7908b9474bb9674cf0"}, + {file = "contourpy-1.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:462c59914dc6d81e0b11f37e560b8a7c2dbab6aca4f38be31519d442d6cde1a1"}, + {file = "contourpy-1.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6d0a8efc258659edc5299f9ef32d8d81de8b53b45d67bf4bfa3067f31366764d"}, + {file = "contourpy-1.1.1-cp310-cp310-win32.whl", hash = "sha256:d6ab42f223e58b7dac1bb0af32194a7b9311065583cc75ff59dcf301afd8a431"}, + {file = "contourpy-1.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:549174b0713d49871c6dee90a4b499d3f12f5e5f69641cd23c50a4542e2ca1eb"}, + {file = "contourpy-1.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:407d864db716a067cc696d61fa1ef6637fedf03606e8417fe2aeed20a061e6b2"}, + {file = "contourpy-1.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dfe80c017973e6a4c367e037cb31601044dd55e6bfacd57370674867d15a899b"}, + {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e30aaf2b8a2bac57eb7e1650df1b3a4130e8d0c66fc2f861039d507a11760e1b"}, + {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3de23ca4f381c3770dee6d10ead6fff524d540c0f662e763ad1530bde5112532"}, + {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:566f0e41df06dfef2431defcfaa155f0acfa1ca4acbf8fd80895b1e7e2ada40e"}, + {file = "contourpy-1.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b04c2f0adaf255bf756cf08ebef1be132d3c7a06fe6f9877d55640c5e60c72c5"}, + {file = "contourpy-1.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0c188ae66b772d9d61d43c6030500344c13e3f73a00d1dc241da896f379bb62"}, + {file = "contourpy-1.1.1-cp311-cp311-win32.whl", hash = "sha256:0683e1ae20dc038075d92e0e0148f09ffcefab120e57f6b4c9c0f477ec171f33"}, + {file = "contourpy-1.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:8636cd2fc5da0fb102a2504fa2c4bea3cbc149533b345d72cdf0e7a924decc45"}, + {file = "contourpy-1.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:560f1d68a33e89c62da5da4077ba98137a5e4d3a271b29f2f195d0fba2adcb6a"}, + {file = "contourpy-1.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:24216552104ae8f3b34120ef84825400b16eb6133af2e27a190fdc13529f023e"}, + {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56de98a2fb23025882a18b60c7f0ea2d2d70bbbcfcf878f9067234b1c4818442"}, + {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:07d6f11dfaf80a84c97f1a5ba50d129d9303c5b4206f776e94037332e298dda8"}, + {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1eaac5257a8f8a047248d60e8f9315c6cff58f7803971170d952555ef6344a7"}, + {file = "contourpy-1.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19557fa407e70f20bfaba7d55b4d97b14f9480856c4fb65812e8a05fe1c6f9bf"}, + {file = "contourpy-1.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:081f3c0880712e40effc5f4c3b08feca6d064cb8cfbb372ca548105b86fd6c3d"}, + {file = "contourpy-1.1.1-cp312-cp312-win32.whl", hash = "sha256:059c3d2a94b930f4dafe8105bcdc1b21de99b30b51b5bce74c753686de858cb6"}, + {file = "contourpy-1.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:f44d78b61740e4e8c71db1cf1fd56d9050a4747681c59ec1094750a658ceb970"}, + {file = "contourpy-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70e5a10f8093d228bb2b552beeb318b8928b8a94763ef03b858ef3612b29395d"}, + {file = "contourpy-1.1.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8394e652925a18ef0091115e3cc191fef350ab6dc3cc417f06da66bf98071ae9"}, + {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5bd5680f844c3ff0008523a71949a3ff5e4953eb7701b28760805bc9bcff217"}, + {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66544f853bfa85c0d07a68f6c648b2ec81dafd30f272565c37ab47a33b220684"}, + {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0c02b75acfea5cab07585d25069207e478d12309557f90a61b5a3b4f77f46ce"}, + {file = "contourpy-1.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:41339b24471c58dc1499e56783fedc1afa4bb018bcd035cfb0ee2ad2a7501ef8"}, + {file = "contourpy-1.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f29fb0b3f1217dfe9362ec55440d0743fe868497359f2cf93293f4b2701b8251"}, + {file = "contourpy-1.1.1-cp38-cp38-win32.whl", hash = "sha256:f9dc7f933975367251c1b34da882c4f0e0b2e24bb35dc906d2f598a40b72bfc7"}, + {file = "contourpy-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:498e53573e8b94b1caeb9e62d7c2d053c263ebb6aa259c81050766beb50ff8d9"}, + {file = "contourpy-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ba42e3810999a0ddd0439e6e5dbf6d034055cdc72b7c5c839f37a7c274cb4eba"}, + {file = "contourpy-1.1.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c06e4c6e234fcc65435223c7b2a90f286b7f1b2733058bdf1345d218cc59e34"}, + {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6fab080484e419528e98624fb5c4282148b847e3602dc8dbe0cb0669469887"}, + {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93df44ab351119d14cd1e6b52a5063d3336f0754b72736cc63db59307dabb718"}, + {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eafbef886566dc1047d7b3d4b14db0d5b7deb99638d8e1be4e23a7c7ac59ff0f"}, + {file = "contourpy-1.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efe0fab26d598e1ec07d72cf03eaeeba8e42b4ecf6b9ccb5a356fde60ff08b85"}, + {file = "contourpy-1.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f08e469821a5e4751c97fcd34bcb586bc243c39c2e39321822060ba902eac49e"}, + {file = "contourpy-1.1.1-cp39-cp39-win32.whl", hash = "sha256:bfc8a5e9238232a45ebc5cb3bfee71f1167064c8d382cadd6076f0d51cff1da0"}, + {file = "contourpy-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c84fdf3da00c2827d634de4fcf17e3e067490c4aea82833625c4c8e6cdea0887"}, + {file = "contourpy-1.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:229a25f68046c5cf8067d6d6351c8b99e40da11b04d8416bf8d2b1d75922521e"}, + {file = "contourpy-1.1.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a10dab5ea1bd4401c9483450b5b0ba5416be799bbd50fc7a6cc5e2a15e03e8a3"}, + {file = "contourpy-1.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:4f9147051cb8fdb29a51dc2482d792b3b23e50f8f57e3720ca2e3d438b7adf23"}, + {file = "contourpy-1.1.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a75cc163a5f4531a256f2c523bd80db509a49fc23721b36dd1ef2f60ff41c3cb"}, + {file = "contourpy-1.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b53d5769aa1f2d4ea407c65f2d1d08002952fac1d9e9d307aa2e1023554a163"}, + {file = "contourpy-1.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11b836b7dbfb74e049c302bbf74b4b8f6cb9d0b6ca1bf86cfa8ba144aedadd9c"}, + {file = "contourpy-1.1.1.tar.gz", hash = "sha256:96ba37c2e24b7212a77da85004c38e7c4d155d3e72a45eeaf22c1f03f607e8ab"}, +] + +[package.dependencies] +numpy = {version = ">=1.16,<2.0", markers = "python_version <= \"3.11\""} + +[package.extras] +bokeh = ["bokeh", "selenium"] +docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.4.1)", "types-Pillow"] +test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] +test-no-images = ["pytest", "pytest-cov", "wurlitzer"] + [[package]] name = "cycler" -version = "0.11.0" +version = "0.12.1" description = "Composable style cycles" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "cycler-0.11.0-py3-none-any.whl", hash = "sha256:3a27e95f763a428a739d2add979fa7494c912a32c17c4c38c4d5f082cad165a3"}, - {file = "cycler-0.11.0.tar.gz", hash = "sha256:9c87405839a19696e837b3b818fed3f5f69f16f1eec1a1ad77e043dcea9c772f"}, + {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, + {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, ] +[package.extras] +docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] +tests = ["pytest", "pytest-cov", "pytest-xdist"] + [[package]] name = "distlib" version = "0.3.7" @@ -248,13 +394,13 @@ files = [ [[package]] name = "docutils" -version = "0.19" +version = "0.20.1" description = "Docutils -- Python Documentation Utilities" optional = false python-versions = ">=3.7" files = [ - {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, - {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, + {file = "docutils-0.20.1-py3-none-any.whl", hash = "sha256:96f387a2c5562db4476f09f13bbab2192e764cac08ebbf3a34a95d9b1e4a59d6"}, + {file = "docutils-0.20.1.tar.gz", hash = "sha256:f08a4e276c3a1583a86dce3e34aba3fe04d02bba2dd51ed16106244e8a923e3b"}, ] [[package]] @@ -301,32 +447,73 @@ testing = ["hatch", "pre-commit", "pytest", "tox"] [[package]] name = "filelock" -version = "3.12.2" +version = "3.12.4" description = "A platform independent file lock." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, - {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, + {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.5.20)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] +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 = "fonttools" -version = "4.38.0" +version = "4.43.1" description = "Tools to manipulate font files" optional = false -python-versions = ">=3.7" -files = [ - {file = "fonttools-4.38.0-py3-none-any.whl", hash = "sha256:820466f43c8be8c3009aef8b87e785014133508f0de64ec469e4efb643ae54fb"}, - {file = "fonttools-4.38.0.zip", hash = "sha256:2bb244009f9bf3fa100fc3ead6aeb99febe5985fa20afbfbaa2f8946c2fbdaf1"}, +python-versions = ">=3.8" +files = [ + {file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf11e2cca121df35e295bd34b309046c29476ee739753bc6bc9d5050de319273"}, + {file = "fonttools-4.43.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:10b3922875ffcba636674f406f9ab9a559564fdbaa253d66222019d569db869c"}, + {file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9f727c3e3d08fd25352ed76cc3cb61486f8ed3f46109edf39e5a60fc9fecf6ca"}, + {file = "fonttools-4.43.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad0b3f6342cfa14be996971ea2b28b125ad681c6277c4cd0fbdb50340220dfb6"}, + {file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3b7ad05b2beeebafb86aa01982e9768d61c2232f16470f9d0d8e385798e37184"}, + {file = "fonttools-4.43.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4c54466f642d2116686268c3e5f35ebb10e49b0d48d41a847f0e171c785f7ac7"}, + {file = "fonttools-4.43.1-cp310-cp310-win32.whl", hash = "sha256:1e09da7e8519e336239fbd375156488a4c4945f11c4c5792ee086dd84f784d02"}, + {file = "fonttools-4.43.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cf9e974f63b1080b1d2686180fc1fbfd3bfcfa3e1128695b5de337eb9075cef"}, + {file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5db46659cfe4e321158de74c6f71617e65dc92e54980086823a207f1c1c0e24b"}, + {file = "fonttools-4.43.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1952c89a45caceedf2ab2506d9a95756e12b235c7182a7a0fff4f5e52227204f"}, + {file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c36da88422e0270fbc7fd959dc9749d31a958506c1d000e16703c2fce43e3d0"}, + {file = "fonttools-4.43.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bbbf8174501285049e64d174e29f9578495e1b3b16c07c31910d55ad57683d8"}, + {file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d4071bd1c183b8d0b368cc9ed3c07a0f6eb1bdfc4941c4c024c49a35429ac7cd"}, + {file = "fonttools-4.43.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d21099b411e2006d3c3e1f9aaf339e12037dbf7bf9337faf0e93ec915991f43b"}, + {file = "fonttools-4.43.1-cp311-cp311-win32.whl", hash = "sha256:b84a1c00f832feb9d0585ca8432fba104c819e42ff685fcce83537e2e7e91204"}, + {file = "fonttools-4.43.1-cp311-cp311-win_amd64.whl", hash = "sha256:9a2f0aa6ca7c9bc1058a9d0b35483d4216e0c1bbe3962bc62ce112749954c7b8"}, + {file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4d9740e3783c748521e77d3c397dc0662062c88fd93600a3c2087d3d627cd5e5"}, + {file = "fonttools-4.43.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:884ef38a5a2fd47b0c1291647b15f4e88b9de5338ffa24ee52c77d52b4dfd09c"}, + {file = "fonttools-4.43.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9648518ef687ba818db3fcc5d9aae27a369253ac09a81ed25c3867e8657a0680"}, + {file = "fonttools-4.43.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e974d70238fc2be5f444fa91f6347191d0e914d5d8ae002c9aa189572cc215"}, + {file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:34f713dad41aa21c637b4e04fe507c36b986a40f7179dcc86402237e2d39dcd3"}, + {file = "fonttools-4.43.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:360201d46165fc0753229afe785900bc9596ee6974833124f4e5e9f98d0f592b"}, + {file = "fonttools-4.43.1-cp312-cp312-win32.whl", hash = "sha256:bb6d2f8ef81ea076877d76acfb6f9534a9c5f31dc94ba70ad001267ac3a8e56f"}, + {file = "fonttools-4.43.1-cp312-cp312-win_amd64.whl", hash = "sha256:25d3da8a01442cbc1106490eddb6d31d7dffb38c1edbfabbcc8db371b3386d72"}, + {file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8da417431bfc9885a505e86ba706f03f598c85f5a9c54f67d63e84b9948ce590"}, + {file = "fonttools-4.43.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:51669b60ee2a4ad6c7fc17539a43ffffc8ef69fd5dbed186a38a79c0ac1f5db7"}, + {file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:748015d6f28f704e7d95cd3c808b483c5fb87fd3eefe172a9da54746ad56bfb6"}, + {file = "fonttools-4.43.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7a58eb5e736d7cf198eee94844b81c9573102ae5989ebcaa1d1a37acd04b33d"}, + {file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6bb5ea9076e0e39defa2c325fc086593ae582088e91c0746bee7a5a197be3da0"}, + {file = "fonttools-4.43.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f37e31291bf99a63328668bb83b0669f2688f329c4c0d80643acee6e63cd933"}, + {file = "fonttools-4.43.1-cp38-cp38-win32.whl", hash = "sha256:9c60ecfa62839f7184f741d0509b5c039d391c3aff71dc5bc57b87cc305cff3b"}, + {file = "fonttools-4.43.1-cp38-cp38-win_amd64.whl", hash = "sha256:fe9b1ec799b6086460a7480e0f55c447b1aca0a4eecc53e444f639e967348896"}, + {file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13a9a185259ed144def3682f74fdcf6596f2294e56fe62dfd2be736674500dba"}, + {file = "fonttools-4.43.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b2adca1b46d69dce4a37eecc096fe01a65d81a2f5c13b25ad54d5430ae430b13"}, + {file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18eefac1b247049a3a44bcd6e8c8fd8b97f3cad6f728173b5d81dced12d6c477"}, + {file = "fonttools-4.43.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2062542a7565091cea4cc14dd99feff473268b5b8afdee564f7067dd9fff5860"}, + {file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18a2477c62a728f4d6e88c45ee9ee0229405e7267d7d79ce1f5ce0f3e9f8ab86"}, + {file = "fonttools-4.43.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a7a06f8d95b7496e53af80d974d63516ffb263a468e614978f3899a6df52d4b3"}, + {file = "fonttools-4.43.1-cp39-cp39-win32.whl", hash = "sha256:10003ebd81fec0192c889e63a9c8c63f88c7d72ae0460b7ba0cd2a1db246e5ad"}, + {file = "fonttools-4.43.1-cp39-cp39-win_amd64.whl", hash = "sha256:e117a92b07407a061cde48158c03587ab97e74e7d73cb65e6aadb17af191162a"}, + {file = "fonttools-4.43.1-py3-none-any.whl", hash = "sha256:4f88cae635bfe4bbbdc29d479a297bb525a94889184bb69fa9560c2d4834ddb9"}, + {file = "fonttools-4.43.1.tar.gz", hash = "sha256:17dbc2eeafb38d5d0e865dcce16e313c58265a6d2d20081c435f84dc5a9d8212"}, ] [package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=14.0.0)", "xattr", "zopfli (>=0.1.4)"] +all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0,<5)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.0.0)", "xattr", "zopfli (>=0.1.4)"] graphite = ["lz4 (>=1.7.4.2)"] interpolatable = ["munkres", "scipy"] lxml = ["lxml (>=4.0,<5)"] @@ -336,18 +523,18 @@ repacker = ["uharfbuzz (>=0.23.0)"] symfont = ["sympy"] type1 = ["xattr"] ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=14.0.0)"] +unicode = ["unicodedata2 (>=15.0.0)"] woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "identify" -version = "2.5.24" +version = "2.5.30" description = "File identification library for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "identify-2.5.24-py2.py3-none-any.whl", hash = "sha256:986dbfb38b1140e763e413e6feb44cd731faf72d1909543178aa79b0e258265d"}, - {file = "identify-2.5.24.tar.gz", hash = "sha256:0aac67d5b4812498056d28a9a512a483f5085cc28640b02b258a59dac34301d4"}, + {file = "identify-2.5.30-py2.py3-none-any.whl", hash = "sha256:afe67f26ae29bab007ec21b03d4114f41316ab9dd15aa8736a167481e108da54"}, + {file = "identify-2.5.30.tar.gz", hash = "sha256:f302a4256a15c849b91cfcdcec052a8ce914634b2f77ae87dad29cd749f2d88d"}, ] [package.extras] @@ -377,41 +564,40 @@ files = [ [[package]] name = "importlib-metadata" -version = "6.7.0" +version = "6.8.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, - {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, + {file = "importlib_metadata-6.8.0-py3-none-any.whl", hash = "sha256:3ebb78df84a805d7698245025b975d9d67053cd94c79245ba4b3eb694abe68bb"}, + {file = "importlib_metadata-6.8.0.tar.gz", hash = "sha256:dbace7892d8c0c4ac1ad096662232f831d4e64f4c4545bd53016a3e9d4654743"}, ] [package.dependencies] -typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] [[package]] name = "importlib-resources" -version = "5.12.0" +version = "6.1.0" description = "Read resources from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, - {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, + {file = "importlib_resources-6.1.0-py3-none-any.whl", hash = "sha256:aa50258bbfa56d4e33fbd8aa3ef48ded10d1735f11532b8df95388cc6bdb7e83"}, + {file = "importlib_resources-6.1.0.tar.gz", hash = "sha256:9d48dcccc213325e810fd723e7fbb45ccb39f6cf5c31f00cf2b965f5f10f3cb9"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] +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)"] [[package]] name = "iniconfig" @@ -426,17 +612,17 @@ files = [ [[package]] name = "isort" -version = "5.11.5" +version = "5.12.0" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.7.0" +python-versions = ">=3.8.0" files = [ - {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, - {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, + {file = "isort-5.12.0-py3-none-any.whl", hash = "sha256:f84c2818376e66cf843d497486ea8fed8700b340f308f076c6fb1229dff318b6"}, + {file = "isort-5.12.0.tar.gz", hash = "sha256:8bef7dde241278824a6d83f44a544709b065191b95b6e50894bdc722fcba0504"}, ] [package.extras] -colors = ["colorama (>=0.4.3,<0.5.0)"] +colors = ["colorama (>=0.4.3)"] pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] requirements-deprecated-finder = ["pip-api", "pipreqs"] @@ -460,27 +646,42 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "jsonschema" -version = "4.17.3" +version = "4.19.1" description = "An implementation of JSON Schema validation for Python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, - {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, + {file = "jsonschema-4.19.1-py3-none-any.whl", hash = "sha256:cd5f1f9ed9444e554b38ba003af06c0a8c2868131e56bfbef0550fb450c0330e"}, + {file = "jsonschema-4.19.1.tar.gz", hash = "sha256:ec84cc37cfa703ef7cd4928db24f9cb31428a5d0fa77747b8b51a847458e0bbf"}, ] [package.dependencies] -attrs = ">=17.4.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +attrs = ">=22.2.0" importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +jsonschema-specifications = ">=2023.03.6" pkgutil-resolve-name = {version = ">=1.3.10", markers = "python_version < \"3.9\""} -pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} +referencing = ">=0.28.4" +rpds-py = ">=0.7.1" [package.extras] format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] +[[package]] +name = "jsonschema-specifications" +version = "2023.7.1" +description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" +optional = false +python-versions = ">=3.8" +files = [ + {file = "jsonschema_specifications-2023.7.1-py3-none-any.whl", hash = "sha256:05adf340b659828a004220a9613be00fa3f223f2b82002e273dee62fd50524b1"}, + {file = "jsonschema_specifications-2023.7.1.tar.gz", hash = "sha256:c91a50404e88a1f6ba40636778e2ee08f6e24c5613fe4c53ac24578a5a7f72bb"}, +] + +[package.dependencies] +importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""} +referencing = ">=0.28.0" + [[package]] name = "kiwisolver" version = "1.4.5" @@ -594,9 +795,6 @@ files = [ {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, ] -[package.dependencies] -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} - [[package]] name = "lxml" version = "4.9.3" @@ -706,18 +904,17 @@ source = ["Cython (>=0.29.35)"] [[package]] name = "markdown-it-py" -version = "2.2.0" +version = "3.0.0" description = "Python port of markdown-it. Markdown parsing, done right!" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "markdown-it-py-2.2.0.tar.gz", hash = "sha256:7c9a5e412688bc771c67432cbfebcdd686c93ce6484913dccf06cb5a0bea35a1"}, - {file = "markdown_it_py-2.2.0-py3-none-any.whl", hash = "sha256:5a35f8d1870171d9acc47b99612dc146129b631baf04970128b568f190d0cc30"}, + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, ] [package.dependencies] mdurl = ">=0.1,<1.0" -typing_extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.extras] benchmarking = ["psutil", "pytest", "pytest-benchmark"] @@ -726,7 +923,7 @@ compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0 linkify = ["linkify-it-py (>=1,<3)"] plugins = ["mdit-py-plugins"] profiling = ["gprof2dot"] -rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] @@ -790,57 +987,72 @@ files = [ [[package]] name = "matplotlib" -version = "3.5.3" +version = "3.7.3" description = "Python plotting package" optional = false -python-versions = ">=3.7" -files = [ - {file = "matplotlib-3.5.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a206a1b762b39398efea838f528b3a6d60cdb26fe9d58b48265787e29cd1d693"}, - {file = "matplotlib-3.5.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cd45a6f3e93a780185f70f05cf2a383daed13c3489233faad83e81720f7ede24"}, - {file = "matplotlib-3.5.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d62880e1f60e5a30a2a8484432bcb3a5056969dc97258d7326ad465feb7ae069"}, - {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ab29589cef03bc88acfa3a1490359000c18186fc30374d8aa77d33cc4a51a4a"}, - {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2886cc009f40e2984c083687251821f305d811d38e3df8ded414265e4583f0c5"}, - {file = "matplotlib-3.5.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c995f7d9568f18b5db131ab124c64e51b6820a92d10246d4f2b3f3a66698a15b"}, - {file = "matplotlib-3.5.3-cp310-cp310-win32.whl", hash = "sha256:6bb93a0492d68461bd458eba878f52fdc8ac7bdb6c4acdfe43dba684787838c2"}, - {file = "matplotlib-3.5.3-cp310-cp310-win_amd64.whl", hash = "sha256:2e6d184ebe291b9e8f7e78bbab7987d269c38ea3e062eace1fe7d898042ef804"}, - {file = "matplotlib-3.5.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6ea6aef5c4338e58d8d376068e28f80a24f54e69f09479d1c90b7172bad9f25b"}, - {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:839d47b8ead7ad9669aaacdbc03f29656dc21f0d41a6fea2d473d856c39c8b1c"}, - {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3b4fa56159dc3c7f9250df88f653f085068bcd32dcd38e479bba58909254af7f"}, - {file = "matplotlib-3.5.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:94ff86af56a3869a4ae26a9637a849effd7643858a1a04dd5ee50e9ab75069a7"}, - {file = "matplotlib-3.5.3-cp37-cp37m-win32.whl", hash = "sha256:35a8ad4dddebd51f94c5d24bec689ec0ec66173bf614374a1244c6241c1595e0"}, - {file = "matplotlib-3.5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:43e9d3fa077bf0cc95ded13d331d2156f9973dce17c6f0c8b49ccd57af94dbd9"}, - {file = "matplotlib-3.5.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:22227c976ad4dc8c5a5057540421f0d8708c6560744ad2ad638d48e2984e1dbc"}, - {file = "matplotlib-3.5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bf618a825deb6205f015df6dfe6167a5d9b351203b03fab82043ae1d30f16511"}, - {file = "matplotlib-3.5.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9befa5954cdbc085e37d974ff6053da269474177921dd61facdad8023c4aeb51"}, - {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3840c280ebc87a48488a46f760ea1c0c0c83fcf7abbe2e6baf99d033fd35fd8"}, - {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dacddf5bfcec60e3f26ec5c0ae3d0274853a258b6c3fc5ef2f06a8eb23e042be"}, - {file = "matplotlib-3.5.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b428076a55fb1c084c76cb93e68006f27d247169f056412607c5c88828d08f88"}, - {file = "matplotlib-3.5.3-cp38-cp38-win32.whl", hash = "sha256:874df7505ba820e0400e7091199decf3ff1fde0583652120c50cd60d5820ca9a"}, - {file = "matplotlib-3.5.3-cp38-cp38-win_amd64.whl", hash = "sha256:b28de401d928890187c589036857a270a032961411934bdac4cf12dde3d43094"}, - {file = "matplotlib-3.5.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3211ba82b9f1518d346f6309df137b50c3dc4421b4ed4815d1d7eadc617f45a1"}, - {file = "matplotlib-3.5.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6fe807e8a22620b4cd95cfbc795ba310dc80151d43b037257250faf0bfcd82bc"}, - {file = "matplotlib-3.5.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5c096363b206a3caf43773abebdbb5a23ea13faef71d701b21a9c27fdcef72f4"}, - {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bcdfcb0f976e1bac6721d7d457c17be23cf7501f977b6a38f9d38a3762841f7"}, - {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1e64ac9be9da6bfff0a732e62116484b93b02a0b4d4b19934fb4f8e7ad26ad6a"}, - {file = "matplotlib-3.5.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:73dd93dc35c85dece610cca8358003bf0760d7986f70b223e2306b4ea6d1406b"}, - {file = "matplotlib-3.5.3-cp39-cp39-win32.whl", hash = "sha256:879c7e5fce4939c6aa04581dfe08d57eb6102a71f2e202e3314d5fbc072fd5a0"}, - {file = "matplotlib-3.5.3-cp39-cp39-win_amd64.whl", hash = "sha256:ab8d26f07fe64f6f6736d635cce7bfd7f625320490ed5bfc347f2cdb4fae0e56"}, - {file = "matplotlib-3.5.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:99482b83ebf4eb6d5fc6813d7aacdefdd480f0d9c0b52dcf9f1cc3b2c4b3361a"}, - {file = "matplotlib-3.5.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f814504e459c68118bf2246a530ed953ebd18213dc20e3da524174d84ed010b2"}, - {file = "matplotlib-3.5.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57f1b4e69f438a99bb64d7f2c340db1b096b41ebaa515cf61ea72624279220ce"}, - {file = "matplotlib-3.5.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:d2484b350bf3d32cae43f85dcfc89b3ed7bd2bcd781ef351f93eb6fb2cc483f9"}, - {file = "matplotlib-3.5.3.tar.gz", hash = "sha256:339cac48b80ddbc8bfd05daae0a3a73414651a8596904c2a881cfd1edb65f26c"}, +python-versions = ">=3.8" +files = [ + {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_universal2.whl", hash = "sha256:085c33b27561d9c04386789d5aa5eb4a932ddef43cfcdd0e01735f9a6e85ce0c"}, + {file = "matplotlib-3.7.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c568e80e1c17f68a727f30f591926751b97b98314d8e59804f54f86ae6fa6a22"}, + {file = "matplotlib-3.7.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7baf98c5ad59c5c4743ea884bb025cbffa52dacdfdac0da3e6021a285a90377e"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:236024f582e40dac39bca592258888b38ae47a9fed7b8de652d68d3d02d47d2b"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12b4f6795efea037ce2d41e7c417ad8bd02d5719c6ad4a8450a0708f4a1cfb89"}, + {file = "matplotlib-3.7.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78b2136cc6c5415b78977e0e8c608647d597204b05b1d9089ccf513c7d913733"}, + {file = "matplotlib-3.7.3-cp310-cp310-win32.whl", hash = "sha256:122dcbf9be0086e2a95d9e5e0632dbf3bd5b65eaa68c369363310a6c87753059"}, + {file = "matplotlib-3.7.3-cp310-cp310-win_amd64.whl", hash = "sha256:4aab27d9e33293389e3c1d7c881d414a72bdfda0fedc3a6bf46c6fa88d9b8015"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_universal2.whl", hash = "sha256:d5adc743de91e8e0b13df60deb1b1c285b8effea3d66223afceb14b63c9b05de"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:55de4cf7cd0071b8ebf203981b53ab64f988a0a1f897a2dff300a1124e8bcd8b"}, + {file = "matplotlib-3.7.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ac03377fd908aaee2312d0b11735753e907adb6f4d1d102de5e2425249693f6c"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:755bafc10a46918ce9a39980009b54b02dd249594e5adf52f9c56acfddb5d0b7"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a6094c6f8e8d18db631754df4fe9a34dec3caf074f6869a7db09f18f9b1d6b2"}, + {file = "matplotlib-3.7.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:272dba2f1b107790ed78ebf5385b8d14b27ad9e90419de340364b49fe549a993"}, + {file = "matplotlib-3.7.3-cp311-cp311-win32.whl", hash = "sha256:591c123bed1cb4b9996fb60b41a6d89c2ec4943244540776c5f1283fb6960a53"}, + {file = "matplotlib-3.7.3-cp311-cp311-win_amd64.whl", hash = "sha256:3bf3a178c6504694cee8b88b353df0051583f2f6f8faa146f67115c27c856881"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_universal2.whl", hash = "sha256:edf54cac8ee3603f3093616b40a931e8c063969756a4d78a86e82c2fea9659f7"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:91e36a85ea639a1ba9f91427041eac064b04829945fe331a92617b6cb21d27e5"}, + {file = "matplotlib-3.7.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:caf5eaaf7c68f8d7df269dfbcaf46f48a70ff482bfcebdcc97519671023f2a7d"}, + {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74bf57f505efea376097e948b7cdd87191a7ce8180616390aef496639edf601f"}, + {file = "matplotlib-3.7.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee152a88a0da527840a426535514b6ed8ac4240eb856b1da92cf48124320e346"}, + {file = "matplotlib-3.7.3-cp312-cp312-win_amd64.whl", hash = "sha256:67a410a9c9e07cbc83581eeea144bbe298870bf0ac0ee2f2e10a015ab7efee19"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_universal2.whl", hash = "sha256:259999c05285cb993d7f2a419cea547863fa215379eda81f7254c9e932963729"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3f4e7fd5a6157e1d018ce2166ec8e531a481dd4a36f035b5c23edfe05a25419a"}, + {file = "matplotlib-3.7.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:faa3d12d8811d08d14080a8b7b9caea9a457dc495350166b56df0db4b9909ef5"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:336e88900c11441e458da01c8414fc57e04e17f9d3bb94958a76faa2652bcf6b"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:12f4c0dd8aa280d796c8772ea8265a14f11a04319baa3a16daa5556065e8baea"}, + {file = "matplotlib-3.7.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1990955b11e7918d256cf3b956b10997f405b7917a3f1c7d8e69c1d15c7b1930"}, + {file = "matplotlib-3.7.3-cp38-cp38-win32.whl", hash = "sha256:e78707b751260b42b721507ad7aa60fe4026d7f51c74cca6b9cd8b123ebb633a"}, + {file = "matplotlib-3.7.3-cp38-cp38-win_amd64.whl", hash = "sha256:e594ee43c59ea39ca5c6244667cac9d017a3527febc31f5532ad9135cf7469ec"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_universal2.whl", hash = "sha256:6eaa1cf0e94c936a26b78f6d756c5fbc12e0a58c8a68b7248a2a31456ce4e234"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0a97af9d22e8ebedc9f00b043d9bbd29a375e9e10b656982012dded44c10fd77"}, + {file = "matplotlib-3.7.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1f9c6c16597af660433ab330b59ee2934b832ee1fabcaf5cbde7b2add840f31e"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7240259b4b9cbc62381f6378cff4d57af539162a18e832c1e48042fabc40b6b"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:747c6191d2e88ae854809e69aa358dbf852ff1a5738401b85c1cc9012309897a"}, + {file = "matplotlib-3.7.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec726b08a5275d827aa91bb951e68234a4423adb91cf65bc0fcdc0f2777663f7"}, + {file = "matplotlib-3.7.3-cp39-cp39-win32.whl", hash = "sha256:40e3b9b450c6534f07278310c4e34caff41c2a42377e4b9d47b0f8d3ac1083a2"}, + {file = "matplotlib-3.7.3-cp39-cp39-win_amd64.whl", hash = "sha256:dfc118642903a23e309b1da32886bb39a4314147d013e820c86b5fb4cb2e36d0"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:165c8082bf8fc0360c24aa4724a22eaadbfd8c28bf1ccf7e94d685cad48261e4"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ebd8470cc2a3594746ff0513aecbfa2c55ff6f58e6cef2efb1a54eb87c88ffa2"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7153453669c9672b52095119fd21dd032d19225d48413a2871519b17db4b0fde"}, + {file = "matplotlib-3.7.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:498a08267dc69dd8f24c4b5d7423fa584d7ce0027ba71f7881df05fc09b89bb7"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d48999c4b19b5a0c058c9cd828ff6fc7748390679f6cf9a2ad653a3e802c87d3"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22d65d18b4ee8070a5fea5761d59293f1f9e2fac37ec9ce090463b0e629432fd"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c40cde976c36693cc0767e27cf5f443f91c23520060bd9496678364adfafe9c"}, + {file = "matplotlib-3.7.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:39018a2b17592448fbfdf4b8352955e6c3905359939791d4ff429296494d1a0c"}, + {file = "matplotlib-3.7.3.tar.gz", hash = "sha256:f09b3dd6bdeb588de91f853bbb2d6f0ff8ab693485b0c49035eaa510cb4f142e"}, ] [package.dependencies] +contourpy = ">=1.0.1" cycler = ">=0.10" fonttools = ">=4.22.0" +importlib-resources = {version = ">=3.2.0", markers = "python_version < \"3.10\""} kiwisolver = ">=1.0.1" -numpy = ">=1.17" +numpy = ">=1.20,<2" packaging = ">=20.0" pillow = ">=6.2.0" -pyparsing = ">=2.2.1" +pyparsing = ">=2.3.1" python-dateutil = ">=2.7" +setuptools_scm = ">=7" [[package]] name = "mdurl" @@ -855,46 +1067,51 @@ files = [ [[package]] name = "memray" -version = "1.9.1" +version = "1.10.0" description = "A memory profiler for Python applications" optional = false python-versions = ">=3.7.0" files = [ - {file = "memray-1.9.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:aae3c2b79b4814183ce60e1b6abedc5f56969dcab3f0a8f00d91f9964ae72727"}, - {file = "memray-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:152d4b6b501a83a4cdafe5bd757a0940ebc5ac2b2b091f6f7c306ac9a8209da5"}, - {file = "memray-1.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bf96d09c7c160ce2c05fa3a077da2563b299985e0542833b70f8b19350df5df8"}, - {file = "memray-1.9.1-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3837aed792d90e90743448feae98c3a8a3b4da389aab49c359c9014dfdb8bff9"}, - {file = "memray-1.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a773c0e0a28be72e4df8dbb00cfc0cff40f992fa413e078b861bf0740910c958"}, - {file = "memray-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:939270ede80b6c2e0e56dd75edb6a97b44a80bad977c81c68f32fdb3200ff2d5"}, - {file = "memray-1.9.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1fad0e170264f6f71b7ed83843f896a7a9a4efe5684aa40fe48892dc546c5017"}, - {file = "memray-1.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7af9677277d62b1c59ce8e3d493a9f95f1eea1cb6ac362de1b1faad7bfbb18de"}, - {file = "memray-1.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6512866eb49cea99fd0915d8c86d21cad4bf40050092a986e32c2c8c4d6e0d8f"}, - {file = "memray-1.9.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b3c634dac856f5e5d899a944509116f53c36638559f43dda6255333068274b8"}, - {file = "memray-1.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b80f09e178edbc0de6d41d55592ec209936cccb611638b3e7da2c45693433e2b"}, - {file = "memray-1.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c602969b1e6dc9502a6911b721536a0209423e798b6cdc690fa0f5b8de02653c"}, - {file = "memray-1.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2a0a4b6d48f7a6ac18ba475a1deaba3f1c237ee37547a34680ee449c393aacc"}, - {file = "memray-1.9.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a47a39af7ea6960a54a80069bc912b6879046f6e8310d46d6286568c76aea50a"}, - {file = "memray-1.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3aed29f34bac1a1a6b5ea47700123b3fc2cee2c9c21fe7bc245edcf704d556fd"}, - {file = "memray-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:16dcd1ddb01826dbbacc91fd6b3b01791263e60bde91901d60e48364480aa127"}, - {file = "memray-1.9.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:caec236f873b097eeacc192d20f6fc0490988f37176ea27f0bdf697331353b3b"}, - {file = "memray-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:facfee26da17dfcfbb0b62ff2b7041169ad6017c9747e762d7a7e17725e55124"}, - {file = "memray-1.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f647168ee77af4c447ff73e2584a2e3f3d8e13655aa6c0d33f02fa19947d5cc3"}, - {file = "memray-1.9.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9ac8f6e249395b24b7f7773b791e9686557216f5f12c4cbe97d0e0c8b0494d7e"}, - {file = "memray-1.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a3ce139c103435c3e366ea515d4081a3144a48cf3aae4a5c7bcf2ca7bd35285"}, - {file = "memray-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:3015a28fe6e8cf1d0cd741085da58cc0091ed895161ecacc6a159c14af7a5a35"}, - {file = "memray-1.9.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:0e9532a9046554fa1b5166c4a02c54d35775e745a0a895e33c5b3ce6288116d3"}, - {file = "memray-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c37d2d34b0426051f98597a3732167e3f82e9d37251a1cd73c23b7384e4f842c"}, - {file = "memray-1.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f81dede3d702f4d2030f7985caad145f5ff7f33eddc31b09caa49b42071ba275"}, - {file = "memray-1.9.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aab379c26255bdb4e1d455620f8b8ace1c4e9a91f51de176bfcf9b09e357a7ea"}, - {file = "memray-1.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:320ff2aca8e12001563a5895f40349a1a16751e56c168e84f8f76274e591cf42"}, - {file = "memray-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a8a56e6bc88037c99e899697f312540171042e097e5d2f5688fccaa6bc9008d4"}, - {file = "memray-1.9.1.tar.gz", hash = "sha256:d676ae40ef8ae9c41bf3a27f5fa89c1e9f80d9ba8e9f7e1e7073b5f90b3f265b"}, + {file = "memray-1.10.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:843a688877691746f9d1835cfa8a65139948471bdd78720435808d20bc30a1cc"}, + {file = "memray-1.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6937d7ef67d18ccc01c3250cdf3b4ef1445b859ee8756f09e3d11bd3ff0c7d67"}, + {file = "memray-1.10.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:23e8c402625cfb32d0e9edb5ec0945f3e5e54bc6b0c5699f6284302082b80bd4"}, + {file = "memray-1.10.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f16c5c8730b616613dc8bafe32649ca6bd7252606251eb00148582011758d0b5"}, + {file = "memray-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7aeb47174c42e99740a8e2b3b6fe0932c95d987258d48a746974ead19176c26"}, + {file = "memray-1.10.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2ce59ef485db3634de98b3a026d2450fc0a875e3a58a9ea85f7a89098841defe"}, + {file = "memray-1.10.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:53a8f66af18b1f3bcf5c9f3c95ae4134dd675903a38f9d0e6341b7bca01b63d0"}, + {file = "memray-1.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9627184c926252c8f719c301f1fefe970f0d033c643a6448b93fed2889d1ea94"}, + {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3a14960838d89a91747885897d34134afb65883cc3b0ed7ff30fe1af00f9fe6"}, + {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f2a47871c172a0539bd72737bb6b294fc10c510464066b825d90fcd3bb4916"}, + {file = "memray-1.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3c401c57f49c4c5f1fecaee1e746f537cdc6680da05fb963dc143bd08ee109bf"}, + {file = "memray-1.10.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ce22a887a585ef5020896de89ffc793e531b65ccc81fbafcc7886010c2c562b3"}, + {file = "memray-1.10.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:b75040f28e8678d0e9c4907d55c95cf26db8ef5adc9941a228f1b280a9efd9c0"}, + {file = "memray-1.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:95e563d9c976e429ad597ad2720d95cebbe8bac891a3082465439143e2740772"}, + {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:663d463e89a64bae4a6b2f8c837d11a3d094834442d536a4165e1d31899a3500"}, + {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a21745fb516b7a6efcd40aa7487c59e9313fcfc782d0193fcfcf00b48426874"}, + {file = "memray-1.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf6d683c4f8d25c6ad06ae18715f218983c5eb86803953615e902d632fdf6ec1"}, + {file = "memray-1.10.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b311e91203be71e1a0ce5e4f978137765bcb1045f3bf5646129c83c5b96ab3c"}, + {file = "memray-1.10.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:68bd8df023c8a32f44c11d997e5c536837e27c0955daf557d3a377edd55a1dd3"}, + {file = "memray-1.10.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:322ed0b69014a0969b777768d461a785203f81f9864386b666b5b26645d9c294"}, + {file = "memray-1.10.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9e985fb7646b0475c303919d19211d2aa54e5a9e2cd2a102472299be5dbebd3"}, + {file = "memray-1.10.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4eba29179772b4a2e440a065b320b03bc2e73fe2648bdf7936aa3b9a086fab4a"}, + {file = "memray-1.10.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:b681519357d94f5f0857fbc6029e7c44d3f41436109e955a14fd312d8317bc35"}, + {file = "memray-1.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8196c684f1be8fe423e5cdd2356d4255a2cb482a1f3e89612b70d2a2862cf5bb"}, + {file = "memray-1.10.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:898acd60f57a10dc5aaf1fd64aa2f821f0420114f3f60c3058083788603f173a"}, + {file = "memray-1.10.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fd13ef666c7fced9768d1cfabf71dc6dfa6724935a8dff463495ac2dc5e13a4"}, + {file = "memray-1.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e356af93e3b031c83957e9ac1a653f5aaba5df1e357dd17142f5ed19bb3dc660"}, + {file = "memray-1.10.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92c372cb262eddd23049f945ca9527f0e4cc7c40a070aade1802d066f680885b"}, + {file = "memray-1.10.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:38393c86ce6d0a08e6ec0eb1401d49803b7c0c950c2565386751cdc81568cba8"}, + {file = "memray-1.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3a8bb7fbd8303c4f0017ba7faef6b88f904cda2931ed667cbf3b98f024b3bc44"}, + {file = "memray-1.10.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d56f37a34125684746c13d24bd7a3fb17549b0bb355eb50969eb11e05e3ba62"}, + {file = "memray-1.10.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:85c32d6613d81b075f740e398c4d653e0803cd48e82c33dcd584c109d6782666"}, + {file = "memray-1.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:566602b2143e06b3d592901d98c52ce4599e71aa2555146eeb5cec03506f9498"}, + {file = "memray-1.10.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:391aac6c9f744528d3186bc82d708a1acc83525778f804045d7c96f860f8ec98"}, + {file = "memray-1.10.0.tar.gz", hash = "sha256:38322e052b882790993412f1840517a51818aa55c47037f69915b2007f2c4cee"}, ] [package.dependencies] jinja2 = ">=2.9" rich = ">=11.2.0" -typing-extensions = {version = "*", markers = "python_version < \"3.8.0\""} [package.extras] benchmark = ["asv"] @@ -945,7 +1162,6 @@ files = [ [package.dependencies] mypy-extensions = ">=0.4.3" tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} typing-extensions = ">=3.10" [package.extras] @@ -981,50 +1197,50 @@ setuptools = "*" [[package]] name = "numpy" -version = "1.21.1" -description = "NumPy is the fundamental package for array computing with Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"}, - {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"}, - {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"}, - {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"}, - {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"}, - {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"}, - {file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"}, - {file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"}, - {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"}, - {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"}, - {file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"}, - {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"}, - {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"}, - {file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"}, - {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"}, - {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"}, - {file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"}, - {file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"}, - {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"}, - {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"}, - {file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"}, - {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"}, - {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"}, - {file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"}, - {file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"}, - {file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"}, - {file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"}, - {file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"}, +version = "1.24.4" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, ] [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [[package]] @@ -1040,77 +1256,65 @@ files = [ [[package]] name = "pillow" -version = "9.5.0" +version = "10.1.0" description = "Python Imaging Library (Fork)" optional = false -python-versions = ">=3.7" -files = [ - {file = "Pillow-9.5.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:ace6ca218308447b9077c14ea4ef381ba0b67ee78d64046b3f19cf4e1139ad16"}, - {file = "Pillow-9.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d3d403753c9d5adc04d4694d35cf0391f0f3d57c8e0030aac09d7678fa8030aa"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba1b81ee69573fe7124881762bb4cd2e4b6ed9dd28c9c60a632902fe8db8b38"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe7e1c262d3392afcf5071df9afa574544f28eac825284596ac6db56e6d11062"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f36397bf3f7d7c6a3abdea815ecf6fd14e7fcd4418ab24bae01008d8d8ca15e"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:252a03f1bdddce077eff2354c3861bf437c892fb1832f75ce813ee94347aa9b5"}, - {file = "Pillow-9.5.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:85ec677246533e27770b0de5cf0f9d6e4ec0c212a1f89dfc941b64b21226009d"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b416f03d37d27290cb93597335a2f85ed446731200705b22bb927405320de903"}, - {file = "Pillow-9.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1781a624c229cb35a2ac31cc4a77e28cafc8900733a864870c49bfeedacd106a"}, - {file = "Pillow-9.5.0-cp310-cp310-win32.whl", hash = "sha256:8507eda3cd0608a1f94f58c64817e83ec12fa93a9436938b191b80d9e4c0fc44"}, - {file = "Pillow-9.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:d3c6b54e304c60c4181da1c9dadf83e4a54fd266a99c70ba646a9baa626819eb"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:7ec6f6ce99dab90b52da21cf0dc519e21095e332ff3b399a357c187b1a5eee32"}, - {file = "Pillow-9.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:560737e70cb9c6255d6dcba3de6578a9e2ec4b573659943a5e7e4af13f298f5c"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e88745a55b88a7c64fa49bceff363a1a27d9a64e04019c2281049444a571e3"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d9c206c29b46cfd343ea7cdfe1232443072bbb270d6a46f59c259460db76779a"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfcc2c53c06f2ccb8976fb5c71d448bdd0a07d26d8e07e321c103416444c7ad1"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:a0f9bb6c80e6efcde93ffc51256d5cfb2155ff8f78292f074f60f9e70b942d99"}, - {file = "Pillow-9.5.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:8d935f924bbab8f0a9a28404422da8af4904e36d5c33fc6f677e4c4485515625"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fed1e1cf6a42577953abbe8e6cf2fe2f566daebde7c34724ec8803c4c0cda579"}, - {file = "Pillow-9.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c1170d6b195555644f0616fd6ed929dfcf6333b8675fcca044ae5ab110ded296"}, - {file = "Pillow-9.5.0-cp311-cp311-win32.whl", hash = "sha256:54f7102ad31a3de5666827526e248c3530b3a33539dbda27c6843d19d72644ec"}, - {file = "Pillow-9.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:cfa4561277f677ecf651e2b22dc43e8f5368b74a25a8f7d1d4a3a243e573f2d4"}, - {file = "Pillow-9.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:965e4a05ef364e7b973dd17fc765f42233415974d773e82144c9bbaaaea5d089"}, - {file = "Pillow-9.5.0-cp312-cp312-win32.whl", hash = "sha256:22baf0c3cf0c7f26e82d6e1adf118027afb325e703922c8dfc1d5d0156bb2eeb"}, - {file = "Pillow-9.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:432b975c009cf649420615388561c0ce7cc31ce9b2e374db659ee4f7d57a1f8b"}, - {file = "Pillow-9.5.0-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:5d4ebf8e1db4441a55c509c4baa7a0587a0210f7cd25fcfe74dbbce7a4bd1906"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:375f6e5ee9620a271acb6820b3d1e94ffa8e741c0601db4c0c4d3cb0a9c224bf"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99eb6cafb6ba90e436684e08dad8be1637efb71c4f2180ee6b8f940739406e78"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dfaaf10b6172697b9bceb9a3bd7b951819d1ca339a5ef294d1f1ac6d7f63270"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_aarch64.whl", hash = "sha256:763782b2e03e45e2c77d7779875f4432e25121ef002a41829d8868700d119392"}, - {file = "Pillow-9.5.0-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:35f6e77122a0c0762268216315bf239cf52b88865bba522999dc38f1c52b9b47"}, - {file = "Pillow-9.5.0-cp37-cp37m-win32.whl", hash = "sha256:aca1c196f407ec7cf04dcbb15d19a43c507a81f7ffc45b690899d6a76ac9fda7"}, - {file = "Pillow-9.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322724c0032af6692456cd6ed554bb85f8149214d97398bb80613b04e33769f6"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:a0aa9417994d91301056f3d0038af1199eb7adc86e646a36b9e050b06f526597"}, - {file = "Pillow-9.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:f8286396b351785801a976b1e85ea88e937712ee2c3ac653710a4a57a8da5d9c"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c830a02caeb789633863b466b9de10c015bded434deb3ec87c768e53752ad22a"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fbd359831c1657d69bb81f0db962905ee05e5e9451913b18b831febfe0519082"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8fc330c3370a81bbf3f88557097d1ea26cd8b019d6433aa59f71195f5ddebbf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:7002d0797a3e4193c7cdee3198d7c14f92c0836d6b4a3f3046a64bd1ce8df2bf"}, - {file = "Pillow-9.5.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:229e2c79c00e85989a34b5981a2b67aa079fd08c903f0aaead522a1d68d79e51"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9adf58f5d64e474bed00d69bcd86ec4bcaa4123bfa70a65ce72e424bfb88ed96"}, - {file = "Pillow-9.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:662da1f3f89a302cc22faa9f14a262c2e3951f9dbc9617609a47521c69dd9f8f"}, - {file = "Pillow-9.5.0-cp38-cp38-win32.whl", hash = "sha256:6608ff3bf781eee0cd14d0901a2b9cc3d3834516532e3bd673a0a204dc8615fc"}, - {file = "Pillow-9.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:e49eb4e95ff6fd7c0c402508894b1ef0e01b99a44320ba7d8ecbabefddcc5569"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:482877592e927fd263028c105b36272398e3e1be3269efda09f6ba21fd83ec66"}, - {file = "Pillow-9.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3ded42b9ad70e5f1754fb7c2e2d6465a9c842e41d178f262e08b8c85ed8a1d8e"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c446d2245ba29820d405315083d55299a796695d747efceb5717a8b450324115"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8aca1152d93dcc27dc55395604dcfc55bed5f25ef4c98716a928bacba90d33a3"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:608488bdcbdb4ba7837461442b90ea6f3079397ddc968c31265c1e056964f1ef"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:60037a8db8750e474af7ffc9faa9b5859e6c6d0a50e55c45576bf28be7419705"}, - {file = "Pillow-9.5.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:07999f5834bdc404c442146942a2ecadd1cb6292f5229f4ed3b31e0a108746b1"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a127ae76092974abfbfa38ca2d12cbeddcdeac0fb71f9627cc1135bedaf9d51a"}, - {file = "Pillow-9.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:489f8389261e5ed43ac8ff7b453162af39c3e8abd730af8363587ba64bb2e865"}, - {file = "Pillow-9.5.0-cp39-cp39-win32.whl", hash = "sha256:9b1af95c3a967bf1da94f253e56b6286b50af23392a886720f563c547e48e964"}, - {file = "Pillow-9.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:77165c4a5e7d5a284f10a6efaa39a0ae8ba839da344f20b111d62cc932fa4e5d"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:833b86a98e0ede388fa29363159c9b1a294b0905b5128baf01db683672f230f5"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aaf305d6d40bd9632198c766fb64f0c1a83ca5b667f16c1e79e1661ab5060140"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0852ddb76d85f127c135b6dd1f0bb88dbb9ee990d2cd9aa9e28526c93e794fba"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:91ec6fe47b5eb5a9968c79ad9ed78c342b1f97a091677ba0e012701add857829"}, - {file = "Pillow-9.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:cb841572862f629b99725ebaec3287fc6d275be9b14443ea746c1dd325053cbd"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:c380b27d041209b849ed246b111b7c166ba36d7933ec6e41175fd15ab9eb1572"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7c9af5a3b406a50e313467e3565fc99929717f780164fe6fbb7704edba0cebbe"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5671583eab84af046a397d6d0ba25343c00cd50bce03787948e0fff01d4fd9b1"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:84a6f19ce086c1bf894644b43cd129702f781ba5751ca8572f08aa40ef0ab7b7"}, - {file = "Pillow-9.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1e7723bd90ef94eda669a3c2c19d549874dd5badaeefabefd26053304abe5799"}, - {file = "Pillow-9.5.0.tar.gz", hash = "sha256:bf548479d336726d7a0eceb6e767e179fbde37833ae42794602631a070d630f1"}, +python-versions = ">=3.8" +files = [ + {file = "Pillow-10.1.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1ab05f3db77e98f93964697c8efc49c7954b08dd61cff526b7f2531a22410106"}, + {file = "Pillow-10.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6932a7652464746fcb484f7fc3618e6503d2066d853f68a4bd97193a3996e273"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5f63b5a68daedc54c7c3464508d8c12075e56dcfbd42f8c1bf40169061ae666"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0949b55eb607898e28eaccb525ab104b2d86542a85c74baf3a6dc24002edec2"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ae88931f93214777c7a3aa0a8f92a683f83ecde27f65a45f95f22d289a69e593"}, + {file = "Pillow-10.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b0eb01ca85b2361b09480784a7931fc648ed8b7836f01fb9241141b968feb1db"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d27b5997bdd2eb9fb199982bb7eb6164db0426904020dc38c10203187ae2ff2f"}, + {file = "Pillow-10.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7df5608bc38bd37ef585ae9c38c9cd46d7c81498f086915b0f97255ea60c2818"}, + {file = "Pillow-10.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:41f67248d92a5e0a2076d3517d8d4b1e41a97e2df10eb8f93106c89107f38b57"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1fb29c07478e6c06a46b867e43b0bcdb241b44cc52be9bc25ce5944eed4648e7"}, + {file = "Pillow-10.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2cdc65a46e74514ce742c2013cd4a2d12e8553e3a2563c64879f7c7e4d28bce7"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50d08cd0a2ecd2a8657bd3d82c71efd5a58edb04d9308185d66c3a5a5bed9610"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:062a1610e3bc258bff2328ec43f34244fcec972ee0717200cb1425214fe5b839"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:61f1a9d247317fa08a308daaa8ee7b3f760ab1809ca2da14ecc88ae4257d6172"}, + {file = "Pillow-10.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a646e48de237d860c36e0db37ecaecaa3619e6f3e9d5319e527ccbc8151df061"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:47e5bf85b80abc03be7455c95b6d6e4896a62f6541c1f2ce77a7d2bb832af262"}, + {file = "Pillow-10.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a92386125e9ee90381c3369f57a2a50fa9e6aa8b1cf1d9c4b200d41a7dd8e992"}, + {file = "Pillow-10.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f7c276c05a9767e877a0b4c5050c8bee6a6d960d7f0c11ebda6b99746068c2a"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:a89b8312d51715b510a4fe9fc13686283f376cfd5abca8cd1c65e4c76e21081b"}, + {file = "Pillow-10.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:00f438bb841382b15d7deb9a05cc946ee0f2c352653c7aa659e75e592f6fa17d"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d929a19f5469b3f4df33a3df2983db070ebb2088a1e145e18facbc28cae5b27"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a92109192b360634a4489c0c756364c0c3a2992906752165ecb50544c251312"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:0248f86b3ea061e67817c47ecbe82c23f9dd5d5226200eb9090b3873d3ca32de"}, + {file = "Pillow-10.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9882a7451c680c12f232a422730f986a1fcd808da0fd428f08b671237237d651"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1c3ac5423c8c1da5928aa12c6e258921956757d976405e9467c5f39d1d577a4b"}, + {file = "Pillow-10.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:806abdd8249ba3953c33742506fe414880bad78ac25cc9a9b1c6ae97bedd573f"}, + {file = "Pillow-10.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:eaed6977fa73408b7b8a24e8b14e59e1668cfc0f4c40193ea7ced8e210adf996"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:fe1e26e1ffc38be097f0ba1d0d07fcade2bcfd1d023cda5b29935ae8052bd793"}, + {file = "Pillow-10.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7a7e3daa202beb61821c06d2517428e8e7c1aab08943e92ec9e5755c2fc9ba5e"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fadc71218ad2b8ffe437b54876c9382b4a29e030a05a9879f615091f42ffc2"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa1d323703cfdac2036af05191b969b910d8f115cf53093125e4058f62012c9a"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:912e3812a1dbbc834da2b32299b124b5ddcb664ed354916fd1ed6f193f0e2d01"}, + {file = "Pillow-10.1.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7dbaa3c7de82ef37e7708521be41db5565004258ca76945ad74a8e998c30af8d"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9d7bc666bd8c5a4225e7ac71f2f9d12466ec555e89092728ea0f5c0c2422ea80"}, + {file = "Pillow-10.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baada14941c83079bf84c037e2d8b7506ce201e92e3d2fa0d1303507a8538212"}, + {file = "Pillow-10.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:2ef6721c97894a7aa77723740a09547197533146fba8355e86d6d9a4a1056b14"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:0a026c188be3b443916179f5d04548092e253beb0c3e2ee0a4e2cdad72f66099"}, + {file = "Pillow-10.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:04f6f6149f266a100374ca3cc368b67fb27c4af9f1cc8cb6306d849dcdf12616"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb40c011447712d2e19cc261c82655f75f32cb724788df315ed992a4d65696bb"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1a8413794b4ad9719346cd9306118450b7b00d9a15846451549314a58ac42219"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:c9aeea7b63edb7884b031a35305629a7593272b54f429a9869a4f63a1bf04c34"}, + {file = "Pillow-10.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b4005fee46ed9be0b8fb42be0c20e79411533d1fd58edabebc0dd24626882cfd"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4d0152565c6aa6ebbfb1e5d8624140a440f2b99bf7afaafbdbf6430426497f28"}, + {file = "Pillow-10.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d921bc90b1defa55c9917ca6b6b71430e4286fc9e44c55ead78ca1a9f9eba5f2"}, + {file = "Pillow-10.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cfe96560c6ce2f4c07d6647af2d0f3c54cc33289894ebd88cfbb3bcd5391e256"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:937bdc5a7f5343d1c97dc98149a0be7eb9704e937fe3dc7140e229ae4fc572a7"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c25762197144e211efb5f4e8ad656f36c8d214d390585d1d21281f46d556ba"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:afc8eef765d948543a4775f00b7b8c079b3321d6b675dde0d02afa2ee23000b4"}, + {file = "Pillow-10.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:883f216eac8712b83a63f41b76ddfb7b2afab1b74abbb413c5df6680f071a6b9"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:b920e4d028f6442bea9a75b7491c063f0b9a3972520731ed26c83e254302eb1e"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c41d960babf951e01a49c9746f92c5a7e0d939d1652d7ba30f6b3090f27e412"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1fafabe50a6977ac70dfe829b2d5735fd54e190ab55259ec8aea4aaea412fa0b"}, + {file = "Pillow-10.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b834f4b16173e5b92ab6566f0473bfb09f939ba14b23b8da1f54fa63e4b623f"}, + {file = "Pillow-10.1.0.tar.gz", hash = "sha256:e6bf8de6c36ed96c86ea3b6e1d5273c53f46ef518a062464cd7ef5dd2cf92e38"}, ] [package.extras] @@ -1130,36 +1334,30 @@ files = [ [[package]] name = "platformdirs" -version = "3.10.0" +version = "3.11.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." optional = false python-versions = ">=3.7" files = [ - {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, - {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, + {file = "platformdirs-3.11.0-py3-none-any.whl", hash = "sha256:e9d171d00af68be50e9202731309c4e658fd8bc76f55c11c7dd760d023bda68e"}, + {file = "platformdirs-3.11.0.tar.gz", hash = "sha256:cf8ee52a3afdb965072dcc652433e0c7e3e40cf5ea1477cd4b3b1d2eb75495b3"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.7.1", markers = "python_version < \"3.8\""} - [package.extras] docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.1)", "sphinx-autodoc-typehints (>=1.24)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)"] [[package]] name = "pluggy" -version = "1.2.0" +version = "1.3.0" description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, - {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, + {file = "pluggy-1.3.0-py3-none-any.whl", hash = "sha256:d89c696a773f8bd377d18e5ecda92b7a3793cbe66c87060a6fb58c7b6e1061f7"}, + {file = "pluggy-1.3.0.tar.gz", hash = "sha256:cf61ae8f126ac6f7c451172cf30e3e43d3ca77615509771b3a984a0730651e12"}, ] -[package.dependencies] -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} - [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] @@ -1178,11 +1376,38 @@ files = [ [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" +[[package]] +name = "psutil" +version = "5.9.6" +description = "Cross-platform lib for process and system monitoring in Python." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +files = [ + {file = "psutil-5.9.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:fb8a697f11b0f5994550555fcfe3e69799e5b060c8ecf9e2f75c69302cc35c0d"}, + {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:91ecd2d9c00db9817a4b4192107cf6954addb5d9d67a969a4f436dbc9200f88c"}, + {file = "psutil-5.9.6-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:10e8c17b4f898d64b121149afb136c53ea8b68c7531155147867b7b1ac9e7e28"}, + {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:18cd22c5db486f33998f37e2bb054cc62fd06646995285e02a51b1e08da97017"}, + {file = "psutil-5.9.6-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:ca2780f5e038379e520281e4c032dddd086906ddff9ef0d1b9dcf00710e5071c"}, + {file = "psutil-5.9.6-cp27-none-win32.whl", hash = "sha256:70cb3beb98bc3fd5ac9ac617a327af7e7f826373ee64c80efd4eb2856e5051e9"}, + {file = "psutil-5.9.6-cp27-none-win_amd64.whl", hash = "sha256:51dc3d54607c73148f63732c727856f5febec1c7c336f8f41fcbd6315cce76ac"}, + {file = "psutil-5.9.6-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c69596f9fc2f8acd574a12d5f8b7b1ba3765a641ea5d60fb4736bf3c08a8214a"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:92e0cc43c524834af53e9d3369245e6cc3b130e78e26100d1f63cdb0abeb3d3c"}, + {file = "psutil-5.9.6-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:748c9dd2583ed86347ed65d0035f45fa8c851e8d90354c122ab72319b5f366f4"}, + {file = "psutil-5.9.6-cp36-cp36m-win32.whl", hash = "sha256:3ebf2158c16cc69db777e3c7decb3c0f43a7af94a60d72e87b2823aebac3d602"}, + {file = "psutil-5.9.6-cp36-cp36m-win_amd64.whl", hash = "sha256:ff18b8d1a784b810df0b0fff3bcb50ab941c3b8e2c8de5726f9c71c601c611aa"}, + {file = "psutil-5.9.6-cp37-abi3-win32.whl", hash = "sha256:a6f01f03bf1843280f4ad16f4bde26b817847b4c1a0db59bf6419807bc5ce05c"}, + {file = "psutil-5.9.6-cp37-abi3-win_amd64.whl", hash = "sha256:6e5fb8dc711a514da83098bc5234264e551ad980cec5f85dabf4d38ed6f15e9a"}, + {file = "psutil-5.9.6-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:daecbcbd29b289aac14ece28eca6a3e60aa361754cf6da3dfb20d4d32b6c7f57"}, + {file = "psutil-5.9.6.tar.gz", hash = "sha256:e4b92ddcd7dd4cdd3f900180ea1e104932c7bce234fb88976e2a3b296441225a"}, +] + +[package.extras] +test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] + [[package]] name = "py-cpuinfo" version = "9.0.0" @@ -1222,42 +1447,6 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] -[[package]] -name = "pyrsistent" -version = "0.19.3" -description = "Persistent/Functional/Immutable data structures" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyrsistent-0.19.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:20460ac0ea439a3e79caa1dbd560344b64ed75e85d8703943e0b66c2a6150e4a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c18264cb84b5e68e7085a43723f9e4c1fd1d935ab240ce02c0324a8e01ccb64"}, - {file = "pyrsistent-0.19.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b774f9288dda8d425adb6544e5903f1fb6c273ab3128a355c6b972b7df39dcf"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win32.whl", hash = "sha256:5a474fb80f5e0d6c9394d8db0fc19e90fa540b82ee52dba7d246a7791712f74a"}, - {file = "pyrsistent-0.19.3-cp310-cp310-win_amd64.whl", hash = "sha256:49c32f216c17148695ca0e02a5c521e28a4ee6c5089f97e34fe24163113722da"}, - {file = "pyrsistent-0.19.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f0774bf48631f3a20471dd7c5989657b639fd2d285b861237ea9e82c36a415a9"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab2204234c0ecd8b9368dbd6a53e83c3d4f3cab10ecaf6d0e772f456c442393"}, - {file = "pyrsistent-0.19.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e42296a09e83028b3476f7073fcb69ffebac0e66dbbfd1bd847d61f74db30f19"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win32.whl", hash = "sha256:64220c429e42a7150f4bfd280f6f4bb2850f95956bde93c6fda1b70507af6ef3"}, - {file = "pyrsistent-0.19.3-cp311-cp311-win_amd64.whl", hash = "sha256:016ad1afadf318eb7911baa24b049909f7f3bb2c5b1ed7b6a8f21db21ea3faa8"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c4db1bd596fefd66b296a3d5d943c94f4fac5bcd13e99bffe2ba6a759d959a28"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aeda827381f5e5d65cced3024126529ddc4289d944f75e090572c77ceb19adbf"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:42ac0b2f44607eb92ae88609eda931a4f0dfa03038c44c772e07f43e738bcac9"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win32.whl", hash = "sha256:e8f2b814a3dc6225964fa03d8582c6e0b6650d68a232df41e3cc1b66a5d2f8d1"}, - {file = "pyrsistent-0.19.3-cp37-cp37m-win_amd64.whl", hash = "sha256:c9bb60a40a0ab9aba40a59f68214eed5a29c6274c83b2cc206a359c4a89fa41b"}, - {file = "pyrsistent-0.19.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a2471f3f8693101975b1ff85ffd19bb7ca7dd7c38f8a81701f67d6b4f97b87d8"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc5d149f31706762c1f8bda2e8c4f8fead6e80312e3692619a75301d3dbb819a"}, - {file = "pyrsistent-0.19.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3311cb4237a341aa52ab8448c27e3a9931e2ee09561ad150ba94e4cfd3fc888c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win32.whl", hash = "sha256:f0e7c4b2f77593871e918be000b96c8107da48444d57005b6a6bc61fb4331b2c"}, - {file = "pyrsistent-0.19.3-cp38-cp38-win_amd64.whl", hash = "sha256:c147257a92374fde8498491f53ffa8f4822cd70c0d85037e09028e478cababb7"}, - {file = "pyrsistent-0.19.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b735e538f74ec31378f5a1e3886a26d2ca6351106b4dfde376a26fc32a044edc"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99abb85579e2165bd8522f0c0138864da97847875ecbd45f3e7e2af569bfc6f2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3a8cb235fa6d3fd7aae6a4f1429bbb1fec1577d978098da1252f0489937786f3"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win32.whl", hash = "sha256:c74bed51f9b41c48366a286395c67f4e894374306b197e62810e0fdaf2364da2"}, - {file = "pyrsistent-0.19.3-cp39-cp39-win_amd64.whl", hash = "sha256:878433581fc23e906d947a6814336eee031a00e6defba224234169ae3d3d6a98"}, - {file = "pyrsistent-0.19.3-py3-none-any.whl", hash = "sha256:ccf0d6bd208f8111179f0c26fdf84ed7c3891982f2edaeae7422575f47e66b64"}, - {file = "pyrsistent-0.19.3.tar.gz", hash = "sha256:1a2994773706bbb4995c31a97bc94f1418314923bd1048c6d964837040376440"}, -] - [[package]] name = "pytest" version = "7.4.2" @@ -1272,7 +1461,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" @@ -1321,6 +1509,21 @@ psutil = ["psutil (>=3.0)"] setproctitle = ["setproctitle"] testing = ["filelock"] +[[package]] +name = "pytest-xprocess" +version = "0.23.0" +description = "A pytest plugin for managing processes across test runs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pytest-xprocess-0.23.0.tar.gz", hash = "sha256:f8d4041a20a1c1ef1943054e4a3df7ac7b43e7f291f640f43c34e9dcc4b34dfa"}, + {file = "pytest_xprocess-0.23.0-py3-none-any.whl", hash = "sha256:709a8de844aca4681e4bd46970020f65fdf75c1fc0d91406ec9e9f64b242991e"}, +] + +[package.dependencies] +psutil = "*" +pytest = ">=2.8" + [[package]] name = "python-dateutil" version = "2.8.2" @@ -1395,6 +1598,21 @@ files = [ {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, ] +[[package]] +name = "referencing" +version = "0.30.2" +description = "JSON Referencing + Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "referencing-0.30.2-py3-none-any.whl", hash = "sha256:449b6669b6121a9e96a7f9e410b245d471e8d48964c67113ce9afe50c8dd7bdf"}, + {file = "referencing-0.30.2.tar.gz", hash = "sha256:794ad8003c65938edcdbc027f1933215e0d0ccc0291e3ce20a4d87432b59efc0"}, +] + +[package.dependencies] +attrs = ">=22.2.0" +rpds-py = ">=0.7.0" + [[package]] name = "requests" version = "2.31.0" @@ -1465,7 +1683,6 @@ files = [ requests = ">=2.22.0,<3.0" toml = "*" types-toml = "*" -typing-extensions = {version = "*", markers = "python_version < \"3.8\""} urllib3 = ">=1.25.10" [package.extras] @@ -1473,13 +1690,13 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rich" -version = "13.5.2" +version = "13.6.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.5.2-py3-none-any.whl", hash = "sha256:146a90b3b6b47cac4a73c12866a499e9817426423f57c5a66949c086191a8808"}, - {file = "rich-13.5.2.tar.gz", hash = "sha256:fb9d6c0a0f643c99eed3875b5377a184132ba9be4d61516a55273d3554d75a39"}, + {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"}, + {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"}, ] [package.dependencies] @@ -1490,21 +1707,151 @@ typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<9)"] +[[package]] +name = "rpds-py" +version = "0.10.6" +description = "Python bindings to Rust's persistent data structures (rpds)" +optional = false +python-versions = ">=3.8" +files = [ + {file = "rpds_py-0.10.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:6bdc11f9623870d75692cc33c59804b5a18d7b8a4b79ef0b00b773a27397d1f6"}, + {file = "rpds_py-0.10.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:26857f0f44f0e791f4a266595a7a09d21f6b589580ee0585f330aaccccb836e3"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7f5e15c953ace2e8dde9824bdab4bec50adb91a5663df08d7d994240ae6fa31"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:61fa268da6e2e1cd350739bb61011121fa550aa2545762e3dc02ea177ee4de35"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c48f3fbc3e92c7dd6681a258d22f23adc2eb183c8cb1557d2fcc5a024e80b094"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0503c5b681566e8b722fe8c4c47cce5c7a51f6935d5c7012c4aefe952a35eed"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:734c41f9f57cc28658d98270d3436dba65bed0cfc730d115b290e970150c540d"}, + {file = "rpds_py-0.10.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a5d7ed104d158c0042a6a73799cf0eb576dfd5fc1ace9c47996e52320c37cb7c"}, + {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e3df0bc35e746cce42579826b89579d13fd27c3d5319a6afca9893a9b784ff1b"}, + {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:73e0a78a9b843b8c2128028864901f55190401ba38aae685350cf69b98d9f7c9"}, + {file = "rpds_py-0.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ed505ec6305abd2c2c9586a7b04fbd4baf42d4d684a9c12ec6110deefe2a063"}, + {file = "rpds_py-0.10.6-cp310-none-win32.whl", hash = "sha256:d97dd44683802000277bbf142fd9f6b271746b4846d0acaf0cefa6b2eaf2a7ad"}, + {file = "rpds_py-0.10.6-cp310-none-win_amd64.whl", hash = "sha256:b455492cab07107bfe8711e20cd920cc96003e0da3c1f91297235b1603d2aca7"}, + {file = "rpds_py-0.10.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:e8cdd52744f680346ff8c1ecdad5f4d11117e1724d4f4e1874f3a67598821069"}, + {file = "rpds_py-0.10.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:66414dafe4326bca200e165c2e789976cab2587ec71beb80f59f4796b786a238"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc435d059f926fdc5b05822b1be4ff2a3a040f3ae0a7bbbe672babb468944722"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8e7f2219cb72474571974d29a191714d822e58be1eb171f229732bc6fdedf0ac"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3953c6926a63f8ea5514644b7afb42659b505ece4183fdaaa8f61d978754349e"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bb2e4826be25e72013916eecd3d30f66fd076110de09f0e750163b416500721"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bf347b495b197992efc81a7408e9a83b931b2f056728529956a4d0858608b80"}, + {file = "rpds_py-0.10.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:102eac53bb0bf0f9a275b438e6cf6904904908562a1463a6fc3323cf47d7a532"}, + {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:40f93086eef235623aa14dbddef1b9fb4b22b99454cb39a8d2e04c994fb9868c"}, + {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e22260a4741a0e7a206e175232867b48a16e0401ef5bce3c67ca5b9705879066"}, + {file = "rpds_py-0.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f4e56860a5af16a0fcfa070a0a20c42fbb2012eed1eb5ceeddcc7f8079214281"}, + {file = "rpds_py-0.10.6-cp311-none-win32.whl", hash = "sha256:0774a46b38e70fdde0c6ded8d6d73115a7c39d7839a164cc833f170bbf539116"}, + {file = "rpds_py-0.10.6-cp311-none-win_amd64.whl", hash = "sha256:4a5ee600477b918ab345209eddafde9f91c0acd931f3776369585a1c55b04c57"}, + {file = "rpds_py-0.10.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:5ee97c683eaface61d38ec9a489e353d36444cdebb128a27fe486a291647aff6"}, + {file = "rpds_py-0.10.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0713631d6e2d6c316c2f7b9320a34f44abb644fc487b77161d1724d883662e31"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5a53f5998b4bbff1cb2e967e66ab2addc67326a274567697379dd1e326bded7"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6a555ae3d2e61118a9d3e549737bb4a56ff0cec88a22bd1dfcad5b4e04759175"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:945eb4b6bb8144909b203a88a35e0a03d22b57aefb06c9b26c6e16d72e5eb0f0"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:52c215eb46307c25f9fd2771cac8135d14b11a92ae48d17968eda5aa9aaf5071"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1b3cd23d905589cb205710b3988fc8f46d4a198cf12862887b09d7aaa6bf9b9"}, + {file = "rpds_py-0.10.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64ccc28683666672d7c166ed465c09cee36e306c156e787acef3c0c62f90da5a"}, + {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:516a611a2de12fbea70c78271e558f725c660ce38e0006f75139ba337d56b1f6"}, + {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9ff93d3aedef11f9c4540cf347f8bb135dd9323a2fc705633d83210d464c579d"}, + {file = "rpds_py-0.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d858532212f0650be12b6042ff4378dc2efbb7792a286bee4489eaa7ba010586"}, + {file = "rpds_py-0.10.6-cp312-none-win32.whl", hash = "sha256:3c4eff26eddac49d52697a98ea01b0246e44ca82ab09354e94aae8823e8bda02"}, + {file = "rpds_py-0.10.6-cp312-none-win_amd64.whl", hash = "sha256:150eec465dbc9cbca943c8e557a21afdcf9bab8aaabf386c44b794c2f94143d2"}, + {file = "rpds_py-0.10.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:cf693eb4a08eccc1a1b636e4392322582db2a47470d52e824b25eca7a3977b53"}, + {file = "rpds_py-0.10.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4134aa2342f9b2ab6c33d5c172e40f9ef802c61bb9ca30d21782f6e035ed0043"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e782379c2028a3611285a795b89b99a52722946d19fc06f002f8b53e3ea26ea9"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2f6da6d842195fddc1cd34c3da8a40f6e99e4a113918faa5e60bf132f917c247"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a9fe992887ac68256c930a2011255bae0bf5ec837475bc6f7edd7c8dfa254e"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b788276a3c114e9f51e257f2a6f544c32c02dab4aa7a5816b96444e3f9ffc336"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:caa1afc70a02645809c744eefb7d6ee8fef7e2fad170ffdeacca267fd2674f13"}, + {file = "rpds_py-0.10.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bddd4f91eede9ca5275e70479ed3656e76c8cdaaa1b354e544cbcf94c6fc8ac4"}, + {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:775049dfa63fb58293990fc59473e659fcafd953bba1d00fc5f0631a8fd61977"}, + {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c6c45a2d2b68c51fe3d9352733fe048291e483376c94f7723458cfd7b473136b"}, + {file = "rpds_py-0.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0699ab6b8c98df998c3eacf51a3b25864ca93dab157abe358af46dc95ecd9801"}, + {file = "rpds_py-0.10.6-cp38-none-win32.whl", hash = "sha256:ebdab79f42c5961682654b851f3f0fc68e6cc7cd8727c2ac4ffff955154123c1"}, + {file = "rpds_py-0.10.6-cp38-none-win_amd64.whl", hash = "sha256:24656dc36f866c33856baa3ab309da0b6a60f37d25d14be916bd3e79d9f3afcf"}, + {file = "rpds_py-0.10.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:0898173249141ee99ffcd45e3829abe7bcee47d941af7434ccbf97717df020e5"}, + {file = "rpds_py-0.10.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9e9184fa6c52a74a5521e3e87badbf9692549c0fcced47443585876fcc47e469"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5752b761902cd15073a527b51de76bbae63d938dc7c5c4ad1e7d8df10e765138"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:99a57006b4ec39dbfb3ed67e5b27192792ffb0553206a107e4aadb39c5004cd5"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09586f51a215d17efdb3a5f090d7cbf1633b7f3708f60a044757a5d48a83b393"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e225a6a14ecf44499aadea165299092ab0cba918bb9ccd9304eab1138844490b"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2039f8d545f20c4e52713eea51a275e62153ee96c8035a32b2abb772b6fc9e5"}, + {file = "rpds_py-0.10.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34ad87a831940521d462ac11f1774edf867c34172010f5390b2f06b85dcc6014"}, + {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dcdc88b6b01015da066da3fb76545e8bb9a6880a5ebf89e0f0b2e3ca557b3ab7"}, + {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:25860ed5c4e7f5e10c496ea78af46ae8d8468e0be745bd233bab9ca99bfd2647"}, + {file = "rpds_py-0.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:7854a207ef77319ec457c1eb79c361b48807d252d94348305db4f4b62f40f7f3"}, + {file = "rpds_py-0.10.6-cp39-none-win32.whl", hash = "sha256:e6fcc026a3f27c1282c7ed24b7fcac82cdd70a0e84cc848c0841a3ab1e3dea2d"}, + {file = "rpds_py-0.10.6-cp39-none-win_amd64.whl", hash = "sha256:e98c4c07ee4c4b3acf787e91b27688409d918212dfd34c872201273fdd5a0e18"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:68fe9199184c18d997d2e4293b34327c0009a78599ce703e15cd9a0f47349bba"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:3339eca941568ed52d9ad0f1b8eb9fe0958fa245381747cecf2e9a78a5539c42"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a360cfd0881d36c6dc271992ce1eda65dba5e9368575663de993eeb4523d895f"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:031f76fc87644a234883b51145e43985aa2d0c19b063e91d44379cd2786144f8"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1f36a9d751f86455dc5278517e8b65580eeee37d61606183897f122c9e51cef3"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:052a832078943d2b2627aea0d19381f607fe331cc0eb5df01991268253af8417"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:023574366002bf1bd751ebaf3e580aef4a468b3d3c216d2f3f7e16fdabd885ed"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:defa2c0c68734f4a82028c26bcc85e6b92cced99866af118cd6a89b734ad8e0d"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:879fb24304ead6b62dbe5034e7b644b71def53c70e19363f3c3be2705c17a3b4"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:53c43e10d398e365da2d4cc0bcaf0854b79b4c50ee9689652cdc72948e86f487"}, + {file = "rpds_py-0.10.6-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:3777cc9dea0e6c464e4b24760664bd8831738cc582c1d8aacf1c3f546bef3f65"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:40578a6469e5d1df71b006936ce95804edb5df47b520c69cf5af264d462f2cbb"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:cf71343646756a072b85f228d35b1d7407da1669a3de3cf47f8bbafe0c8183a4"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:10f32b53f424fc75ff7b713b2edb286fdbfc94bf16317890260a81c2c00385dc"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:81de24a1c51cfb32e1fbf018ab0bdbc79c04c035986526f76c33e3f9e0f3356c"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac17044876e64a8ea20ab132080ddc73b895b4abe9976e263b0e30ee5be7b9c2"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5e8a78bd4879bff82daef48c14d5d4057f6856149094848c3ed0ecaf49f5aec2"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78ca33811e1d95cac8c2e49cb86c0fb71f4d8409d8cbea0cb495b6dbddb30a55"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c63c3ef43f0b3fb00571cff6c3967cc261c0ebd14a0a134a12e83bdb8f49f21f"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:7fde6d0e00b2fd0dbbb40c0eeec463ef147819f23725eda58105ba9ca48744f4"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:79edd779cfc46b2e15b0830eecd8b4b93f1a96649bcb502453df471a54ce7977"}, + {file = "rpds_py-0.10.6-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9164ec8010327ab9af931d7ccd12ab8d8b5dc2f4c6a16cbdd9d087861eaaefa1"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d29ddefeab1791e3c751e0189d5f4b3dbc0bbe033b06e9c333dca1f99e1d523e"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:30adb75ecd7c2a52f5e76af50644b3e0b5ba036321c390b8e7ec1bb2a16dd43c"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd609fafdcdde6e67a139898196698af37438b035b25ad63704fd9097d9a3482"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6eef672de005736a6efd565577101277db6057f65640a813de6c2707dc69f396"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cf4393c7b41abbf07c88eb83e8af5013606b1cdb7f6bc96b1b3536b53a574b8"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad857f42831e5b8d41a32437f88d86ead6c191455a3499c4b6d15e007936d4cf"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d7360573f1e046cb3b0dceeb8864025aa78d98be4bb69f067ec1c40a9e2d9df"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d08f63561c8a695afec4975fae445245386d645e3e446e6f260e81663bfd2e38"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:f0f17f2ce0f3529177a5fff5525204fad7b43dd437d017dd0317f2746773443d"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:442626328600bde1d09dc3bb00434f5374948838ce75c41a52152615689f9403"}, + {file = "rpds_py-0.10.6-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:e9616f5bd2595f7f4a04b67039d890348ab826e943a9bfdbe4938d0eba606971"}, + {file = "rpds_py-0.10.6.tar.gz", hash = "sha256:4ce5a708d65a8dbf3748d2474b580d606b1b9f91b5c6ab2a316e0b0cf7a4ba50"}, +] + [[package]] name = "setuptools" -version = "68.0.0" +version = "68.2.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" +files = [ + {file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"}, + {file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + +[[package]] +name = "setuptools-scm" +version = "8.0.4" +description = "the blessed package to manage your versions by scm tags" +optional = false +python-versions = ">=3.8" files = [ - {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, - {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, + {file = "setuptools-scm-8.0.4.tar.gz", hash = "sha256:b5f43ff6800669595193fd09891564ee9d1d7dcb196cab4b2506d53a2e1c95c7"}, + {file = "setuptools_scm-8.0.4-py3-none-any.whl", hash = "sha256:b47844cd2a84b83b3187a5782c71128c28b4c94cad8bfb871da2784a5cb54c4f"}, ] +[package.dependencies] +packaging = ">=20" +setuptools = "*" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} +typing-extensions = "*" + [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +docs = ["entangled-cli[rich]", "mkdocs", "mkdocs-entangled-plugin", "mkdocs-material", "mkdocstrings[python]", "pygments"] +rich = ["rich"] +test = ["build", "pytest", "rich", "wheel"] [[package]] name = "six" @@ -1530,26 +1877,26 @@ files = [ [[package]] name = "sphinx" -version = "5.3.0" +version = "7.1.2" description = "Python documentation generator" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "Sphinx-5.3.0.tar.gz", hash = "sha256:51026de0a9ff9fc13c05d74913ad66047e104f56a129ff73e174eb5c3ee794b5"}, - {file = "sphinx-5.3.0-py3-none-any.whl", hash = "sha256:060ca5c9f7ba57a08a1219e547b269fadf125ae25b06b9fa7f66768efb652d6d"}, + {file = "sphinx-7.1.2-py3-none-any.whl", hash = "sha256:d170a81825b2fcacb6dfd5a0d7f578a053e45d3f2b153fecc948c37344eb4cbe"}, + {file = "sphinx-7.1.2.tar.gz", hash = "sha256:780f4d32f1d7d1126576e0e5ecc19dc32ab76cd24e950228dcf7b1f6d3d9e22f"}, ] [package.dependencies] alabaster = ">=0.7,<0.8" babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} -docutils = ">=0.14,<0.20" +docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" -Pygments = ">=2.12" -requests = ">=2.5.0" +Pygments = ">=2.13" +requests = ">=2.25.0" snowballstemmer = ">=2.0" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" @@ -1560,8 +1907,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5" [package.extras] docs = ["sphinxcontrib-websupport"] -lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "flake8-simplify", "isort", "mypy (>=0.981)", "sphinx-lint", "types-requests", "types-typed-ast"] -test = ["cython", "html5lib", "pytest (>=4.6)", "typed_ast"] +lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-simplify", "isort", "mypy (>=0.990)", "ruff", "sphinx-lint", "types-requests"] +test = ["cython", "filelock", "html5lib", "pytest (>=4.6)"] [[package]] name = "sphinx-data-viewer" @@ -1578,13 +1925,13 @@ docs = ["sphinx (>=4,<5)"] [[package]] name = "sphinxcontrib-applehelp" -version = "1.0.2" -description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books" +version = "1.0.4" +description = "sphinxcontrib-applehelp is a Sphinx extension which outputs Apple help books" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"}, - {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"}, + {file = "sphinxcontrib-applehelp-1.0.4.tar.gz", hash = "sha256:828f867945bbe39817c210a1abfd1bc4895c8b73fcaade56d45357a348a07d7e"}, + {file = "sphinxcontrib_applehelp-1.0.4-py3-none-any.whl", hash = "sha256:29d341f67fb0f6f586b23ad80e072c8e6ad0b48417db2bde114a4c9746feb228"}, ] [package.extras] @@ -1608,13 +1955,13 @@ test = ["pytest"] [[package]] name = "sphinxcontrib-htmlhelp" -version = "2.0.0" +version = "2.0.1" description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "sphinxcontrib-htmlhelp-2.0.0.tar.gz", hash = "sha256:f5f8bb2d0d629f398bf47d0d69c07bc13b65f75a81ad9e2f71a63d4b7a2f6db2"}, - {file = "sphinxcontrib_htmlhelp-2.0.0-py2.py3-none-any.whl", hash = "sha256:d412243dfb797ae3ec2b59eca0e52dac12e75a241bf0e4eb861e450d06c6ed07"}, + {file = "sphinxcontrib-htmlhelp-2.0.1.tar.gz", hash = "sha256:0cbdd302815330058422b98a113195c9249825d681e18f11e8b1f78a2f11efff"}, + {file = "sphinxcontrib_htmlhelp-2.0.1-py3-none-any.whl", hash = "sha256:c38cb46dccf316c79de6e5515e1770414b797162b23cd3d06e67020e1d2a6903"}, ] [package.extras] @@ -1732,56 +2079,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "typed-ast" -version = "1.5.5" -description = "a fork of Python 2 and 3 ast modules with type comment support" -optional = false -python-versions = ">=3.6" -files = [ - {file = "typed_ast-1.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4bc1efe0ce3ffb74784e06460f01a223ac1f6ab31c6bc0376a21184bf5aabe3b"}, - {file = "typed_ast-1.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5f7a8c46a8b333f71abd61d7ab9255440d4a588f34a21f126bbfc95f6049e686"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:597fc66b4162f959ee6a96b978c0435bd63791e31e4f410622d19f1686d5e769"}, - {file = "typed_ast-1.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d41b7a686ce653e06c2609075d397ebd5b969d821b9797d029fccd71fdec8e04"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5fe83a9a44c4ce67c796a1b466c270c1272e176603d5e06f6afbc101a572859d"}, - {file = "typed_ast-1.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d5c0c112a74c0e5db2c75882a0adf3133adedcdbfd8cf7c9d6ed77365ab90a1d"}, - {file = "typed_ast-1.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:e1a976ed4cc2d71bb073e1b2a250892a6e968ff02aa14c1f40eba4f365ffec02"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c631da9710271cb67b08bd3f3813b7af7f4c69c319b75475436fcab8c3d21bee"}, - {file = "typed_ast-1.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b445c2abfecab89a932b20bd8261488d574591173d07827c1eda32c457358b18"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc95ffaaab2be3b25eb938779e43f513e0e538a84dd14a5d844b8f2932593d88"}, - {file = "typed_ast-1.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61443214d9b4c660dcf4b5307f15c12cb30bdfe9588ce6158f4a005baeb167b2"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6eb936d107e4d474940469e8ec5b380c9b329b5f08b78282d46baeebd3692dc9"}, - {file = "typed_ast-1.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e48bf27022897577d8479eaed64701ecaf0467182448bd95759883300ca818c8"}, - {file = "typed_ast-1.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:83509f9324011c9a39faaef0922c6f720f9623afe3fe220b6d0b15638247206b"}, - {file = "typed_ast-1.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:44f214394fc1af23ca6d4e9e744804d890045d1643dd7e8229951e0ef39429b5"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:118c1ce46ce58fda78503eae14b7664163aa735b620b64b5b725453696f2a35c"}, - {file = "typed_ast-1.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be4919b808efa61101456e87f2d4c75b228f4e52618621c77f1ddcaae15904fa"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:fc2b8c4e1bc5cd96c1a823a885e6b158f8451cf6f5530e1829390b4d27d0807f"}, - {file = "typed_ast-1.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:16f7313e0a08c7de57f2998c85e2a69a642e97cb32f87eb65fbfe88381a5e44d"}, - {file = "typed_ast-1.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:2b946ef8c04f77230489f75b4b5a4a6f24c078be4aed241cfabe9cbf4156e7e5"}, - {file = "typed_ast-1.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:2188bc33d85951ea4ddad55d2b35598b2709d122c11c75cffd529fbc9965508e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0635900d16ae133cab3b26c607586131269f88266954eb04ec31535c9a12ef1e"}, - {file = "typed_ast-1.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57bfc3cf35a0f2fdf0a88a3044aafaec1d2f24d8ae8cd87c4f58d615fb5b6311"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:fe58ef6a764de7b4b36edfc8592641f56e69b7163bba9f9c8089838ee596bfb2"}, - {file = "typed_ast-1.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d09d930c2d1d621f717bb217bf1fe2584616febb5138d9b3e8cdd26506c3f6d4"}, - {file = "typed_ast-1.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:d40c10326893ecab8a80a53039164a224984339b2c32a6baf55ecbd5b1df6431"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:fd946abf3c31fb50eee07451a6aedbfff912fcd13cf357363f5b4e834cc5e71a"}, - {file = "typed_ast-1.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ed4a1a42df8a3dfb6b40c3d2de109e935949f2f66b19703eafade03173f8f437"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:045f9930a1550d9352464e5149710d56a2aed23a2ffe78946478f7b5416f1ede"}, - {file = "typed_ast-1.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:381eed9c95484ceef5ced626355fdc0765ab51d8553fec08661dce654a935db4"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bfd39a41c0ef6f31684daff53befddae608f9daf6957140228a08e51f312d7e6"}, - {file = "typed_ast-1.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8c524eb3024edcc04e288db9541fe1f438f82d281e591c548903d5b77ad1ddd4"}, - {file = "typed_ast-1.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:7f58fabdde8dcbe764cef5e1a7fcb440f2463c1bbbec1cf2a86ca7bc1f95184b"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:042eb665ff6bf020dd2243307d11ed626306b82812aba21836096d229fdc6a10"}, - {file = "typed_ast-1.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:622e4a006472b05cf6ef7f9f2636edc51bda670b7bbffa18d26b255269d3d814"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1efebbbf4604ad1283e963e8915daa240cb4bf5067053cf2f0baadc4d4fb51b8"}, - {file = "typed_ast-1.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0aefdd66f1784c58f65b502b6cf8b121544680456d1cebbd300c2c813899274"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:48074261a842acf825af1968cd912f6f21357316080ebaca5f19abbb11690c8a"}, - {file = "typed_ast-1.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:429ae404f69dc94b9361bb62291885894b7c6fb4640d561179548c849f8492ba"}, - {file = "typed_ast-1.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:335f22ccb244da2b5c296e6f96b06ee9bed46526db0de38d2f0e5a6597b81155"}, - {file = "typed_ast-1.5.5.tar.gz", hash = "sha256:94282f7a354f36ef5dbce0ef3467ebf6a258e370ab33d5b40c249fa996e590dd"}, -] - [[package]] name = "types-docutils" version = "0.20.0.3" @@ -1795,17 +2092,17 @@ files = [ [[package]] name = "types-requests" -version = "2.31.0.2" +version = "2.31.0.10" description = "Typing stubs for requests" optional = false -python-versions = "*" +python-versions = ">=3.7" files = [ - {file = "types-requests-2.31.0.2.tar.gz", hash = "sha256:6aa3f7faf0ea52d728bb18c0a0d1522d9bfd8c72d26ff6f61bfc3d06a411cf40"}, - {file = "types_requests-2.31.0.2-py3-none-any.whl", hash = "sha256:56d181c85b5925cbc59f4489a57e72a8b2166f18273fd8ba7b6fe0c0b986f12a"}, + {file = "types-requests-2.31.0.10.tar.gz", hash = "sha256:dc5852a76f1eaf60eafa81a2e50aefa3d1f015c34cf0cba130930866b1b22a92"}, + {file = "types_requests-2.31.0.10-py3-none-any.whl", hash = "sha256:b32b9a86beffa876c0c3ac99a4cd3b8b51e973fb8e3bd4e0a6bb32c7efad80fc"}, ] [package.dependencies] -types-urllib3 = "*" +urllib3 = ">=2" [[package]] name = "types-setuptools" @@ -1832,37 +2129,26 @@ files = [ {file = "types_toml-0.10.8.7-py3-none-any.whl", hash = "sha256:61951da6ad410794c97bec035d59376ce1cbf4453dc9b6f90477e81e4442d631"}, ] -[[package]] -name = "types-urllib3" -version = "1.26.25.14" -description = "Typing stubs for urllib3" -optional = false -python-versions = "*" -files = [ - {file = "types-urllib3-1.26.25.14.tar.gz", hash = "sha256:229b7f577c951b8c1b92c1bc2b2fdb0b49847bd2af6d1cc2a2e3dd340f3bda8f"}, - {file = "types_urllib3-1.26.25.14-py3-none-any.whl", hash = "sha256:9683bbb7fb72e32bfe9d2be6e04875fbe1b3eeec3cbb4ea231435aa7fd6b4f0e"}, -] - [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [[package]] name = "urllib3" -version = "2.0.4" +version = "2.0.7" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.4-py3-none-any.whl", hash = "sha256:de7df1803967d2c2a98e4b11bb7d6bd9210474c46e8a0401514e3a42a75ebde4"}, - {file = "urllib3-2.0.4.tar.gz", hash = "sha256:8d22f86aae8ef5e410d4f539fde9ce6b2113a001bb4d189e0aed70642d602b11"}, + {file = "urllib3-2.0.7-py3-none-any.whl", hash = "sha256:fdb6d215c776278489906c2f8916e6e7d4f5a9b602ccbcfdf7f016fc8da0596e"}, + {file = "urllib3-2.0.7.tar.gz", hash = "sha256:c97dfde1f7bd43a71c8d2a58e369e9b2bf692d1334ea9f9cae55add7d0dd0f84"}, ] [package.extras] @@ -1885,7 +2171,6 @@ files = [ [package.dependencies] distlib = ">=0.3.7,<1" filelock = ">=3.12.2,<4" -importlib-metadata = {version = ">=6.6", markers = "python_version < \"3.8\""} platformdirs = ">=3.9.1,<4" [package.extras] @@ -1894,20 +2179,20 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "zipp" -version = "3.15.0" +version = "3.17.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, - {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1) +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] [metadata] lock-version = "2.0" -python-versions = ">=3.7.0,<4.0" -content-hash = "74d9e2375488c28c1cd6286e8cf27985fa7fb5847f81f5c678a94a086dbaf626" +python-versions = ">=3.8.0,<4.0" +content-hash = "14e2b2bf6b3e40199eaadecdbaa74a513f1aec1998648848531fadff14c476e6" diff --git a/pyproject.toml b/pyproject.toml index 9ed82e217..82ea03e26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,7 +32,7 @@ packages = [ [tool.poetry.dependencies] -python = ">=3.7.0,<4.0" +python = ">=3.8.0,<4.0" sphinx = ">=5.0" docutils = ">=0.15" @@ -49,6 +49,7 @@ pre-commit = "^2" lxml = "^4.6.5" pytest = "^7" pytest-xdist="*" # parallelisation +pytest-xprocess = ">=0.23.0" syrupy = ">=3,<5" responses = "^0.22.0" requests-mock = ">=1.9.3" diff --git a/sphinx_needs/layout.py b/sphinx_needs/layout.py index 887d2fea8..38b123e50 100644 --- a/sphinx_needs/layout.py +++ b/sphinx_needs/layout.py @@ -397,7 +397,7 @@ def _func_replace(self, section_nodes: List[nodes.Node]) -> List[nodes.Node]: node.replace(child, new_child) # type: ignore[attr-defined] return_nodes.append(node) else: - node_str = str(node) + node_str = node.astext() # func_elements = re.findall(r'<<([a-z_()]*)>>', node_str) node_line = nodes.inline() @@ -486,7 +486,11 @@ def meta(self, name: str, prefix: Optional[str] = None, show_empty: bool = False Returns the specific metadata of a need inside docutils nodes. Usage:: - <> + <> + + .. note:: + + You must escape all rst_content in your function strings! E.g. to get `**` one must use `\\\\*\\\\*`. :param name: name of the need item :param prefix: string as rst-code, will be added infront of the value output diff --git a/tests/conftest.py b/tests/conftest.py index e716f6546..be249306a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -75,34 +75,39 @@ def test_server(xprocess, sphinx_test_tempdir): port = 62343 class Starter(ProcessStarter): - pattern = "Serving HTTP" + pattern = "Serving HTTP on [0-9.]+ port 62343|Address already in use" timeout = 20 terminate_on_interrupt = True - args = ["python", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", addr, port] - - # checks if our server is ready with a ping - - def check_server_connection(): + args = ["python3", "-m", "http.server", "--directory", sphinx_test_tempdir, "--bind", addr, port] + env = {"PYTHONUNBUFFERED": "1"} + + def check_server_connection(log_path: str): + """ + Checks the connection status to a server. + :param log_path: The path to the log file. + :return: True if the server connection is successful, False otherwise. + """ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((addr, port)) sock.close() if result == 0: - print("Serving HTTP on 127.0.0.1 port 62343 (http://127.0.0.1:62343/) ...") + with open(str(log_path), "wb", 0) as stdout: + stdout.write(bytes("Serving HTTP on 127.0.0.1 port 62343 (http://127.0.0.1:62343/) ...\n", "utf8")) return True return False - http_server_process = xprocess.getinfo("http_server") - if not check_server_connection(): + if not check_server_connection(log_path=xprocess.getinfo("http_server").logpath): # Start the process and ensure it is running - logfile = xprocess.ensure("http_server", Starter) # noqa:F841 + _, logfile = xprocess.ensure("http_server", Starter, persist_logs=False) # noqa:F841 + http_server_process = xprocess.getinfo("http_server") server_url = f"http://{addr}:{port}" http_server_process.url = server_url yield http_server_process - # Clean up the process after the tests - http_server_process.terminate() + # clean up whole process tree afterward + xprocess.getinfo("http_server").terminate() def test_js(self) -> Dict[str, Any]: @@ -125,7 +130,7 @@ def test_js(self) -> Dict[str, Any]: "stderr": f"The spec_pattern '{self.spec_pattern}' cannot be found.", } _, out_dir = str(self.outdir).split("sn_test_build_data") - srcdir_url = f"http://localhost:62343/{out_dir.lstrip('/')}/" + srcdir_url = f"http://127.0.0.1:62343/{out_dir.lstrip('/')}/" js_test_config = { "specPattern": cypress_testpath, "supportFile": get_abspath("js_test/cypress/support/e2e.js"), @@ -184,10 +189,10 @@ def sphinx_test_tempdir() -> path: sphinx_test_tempdir = path(tempfile.gettempdir()).joinpath("sn_test_build_data") utils_dir = sphinx_test_tempdir.joinpath("utils") - if not (sphinx_test_tempdir.exists() and sphinx_test_tempdir.isdir()): - sphinx_test_tempdir.makedirs() - if not (utils_dir.exists() and utils_dir.isdir()): - utils_dir.makedirs() + # if not (sphinx_test_tempdir.exists() and sphinx_test_tempdir.isdir()): + sphinx_test_tempdir.makedirs(exist_ok=True) + # if not (utils_dir.exists() and utils_dir.isdir()): + utils_dir.makedirs(exist_ok=True) # copy plantuml.jar to current test tempdir. We want to do this once # since the same plantuml.jar is used for each test diff --git a/tests/doc_test/doc_layout/conf.py b/tests/doc_test/doc_layout/conf.py index 5bc569c33..adcc07d7f 100644 --- a/tests/doc_test/doc_layout/conf.py +++ b/tests/doc_test/doc_layout/conf.py @@ -30,6 +30,13 @@ "side": ['<>'], }, }, + "optional_author": { + "grid": "simple", + "layout": { + "head": ['**<>**'], + "meta": ['**status**: <>', r'<>'], + }, + }, "footer_grid": { "grid": "simple_footer", "layout": { diff --git a/tests/doc_test/doc_layout/index.rst b/tests/doc_test/doc_layout/index.rst index a34a4dac5..ce1cf38ea 100644 --- a/tests/doc_test/doc_layout/index.rst +++ b/tests/doc_test/doc_layout/index.rst @@ -13,5 +13,9 @@ TEST DOCUMENT .. spec:: title_example_layout :layout: example +.. spec:: title_layout_optional_author + :layout: optional_author + :author: some author + .. spec:: title_layout_footer_grid :layout: footer_grid diff --git a/tests/test_layouts.py b/tests/test_layouts.py index e20aca3f7..e886c2fbd 100644 --- a/tests/test_layouts.py +++ b/tests/test_layouts.py @@ -23,8 +23,11 @@ def test_doc_build_html(test_app): assert "title_example_layout" in html needs = extract_needs_from_html(html) - assert len(needs) == 5 + assert len(needs) == 6 + assert ( + 'author: some author' in html + ) assert '' in html # check simple_footer grid layout From f1e86d817d2d008cc66506b401d8fab76d99a749 Mon Sep 17 00:00:00 2001 From: Randy Duodu Date: Tue, 24 Oct 2023 14:43:28 +0000 Subject: [PATCH 14/14] Added jquery-3.6.0.min.js to sphinx-needs JS libraries. --- .gitignore | 3 ++- docs/_templates/layout.html | 5 ----- sphinx_needs/environment.py | 10 ++++++++-- sphinx_needs/libs/html/jquery-3.6.0.min.js | 2 ++ 4 files changed, 12 insertions(+), 8 deletions(-) create mode 100644 sphinx_needs/libs/html/jquery-3.6.0.min.js diff --git a/.gitignore b/.gitignore index 76e8f917d..faa0be334 100644 --- a/.gitignore +++ b/.gitignore @@ -30,7 +30,8 @@ docs/_images/need_bar_*.* .dockerignore .benchmarks/ - +tests/cypress/* +tests/cypress/screenshots need_bar_*.png need_pie_*.png diff --git a/docs/_templates/layout.html b/docs/_templates/layout.html index e3a672ed0..b87849563 100644 --- a/docs/_templates/layout.html +++ b/docs/_templates/layout.html @@ -1,11 +1,6 @@ {# Import the theme's layout. #} {% extends '!layout.html' %} -{%- block extrahead %} - - {{ super() }} -{%- endblock %} - {%- block footer %} {{ super() }} diff --git a/sphinx_needs/environment.py b/sphinx_needs/environment.py index 2b78a170f..65ccdd158 100644 --- a/sphinx_needs/environment.py +++ b/sphinx_needs/environment.py @@ -160,7 +160,7 @@ def install_static_files( def install_lib_static_files(app: Sphinx, env: BuildEnvironment) -> None: """ - Copies ccs and js files from needed js/css libs + Copies css and js files from needed js/css libs :param app: :param env: :return: @@ -186,7 +186,13 @@ def install_lib_static_files(app: Sphinx, env: BuildEnvironment) -> None: # Add the needed datatables js and css file lib_path = Path("sphinx-needs") / "libs" / "html" - for f in ["datatables.min.js", "datatables_loader.js", "datatables.min.css", "sphinx_needs_collapse.js"]: + for f in [ + "jquery-3.6.0.min.js", + "datatables.min.js", + "datatables_loader.js", + "datatables.min.css", + "sphinx_needs_collapse.js", + ]: safe_add_file(lib_path / f, app) diff --git a/sphinx_needs/libs/html/jquery-3.6.0.min.js b/sphinx_needs/libs/html/jquery-3.6.0.min.js new file mode 100644 index 000000000..c4c6022f2 --- /dev/null +++ b/sphinx_needs/libs/html/jquery-3.6.0.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0