From d55df355a3546429736e1b6d4763bb56170556c2 Mon Sep 17 00:00:00 2001 From: Auke Oosterhoff Date: Tue, 4 Jul 2023 17:24:22 +0200 Subject: [PATCH] Add OCPP 2.1 code generator This commit introduces a script that parses the JSON schemas for OCPP 2.1 and generate Python code. Fixes: #452 --- .../actions/setup-python-build-env/action.yml | 2 +- .github/workflows/publish-to-pypi.yml | 2 +- .github/workflows/pull-request.yml | 2 +- .gitignore | 4 + Makefile | 10 +- README.rst | 28 + ocpp/v21/__init__.py | 8 + poetry.lock | 768 +++++++++++++----- pyproject.toml | 1 + scripts/v21/code_generator.py | 411 ++++++++++ scripts/v21/generate_implementation.py | 134 +++ scripts/v21/json_schema_parser.py | 255 ++++++ scripts/v21/tests/__init__.py | 0 .../NotifyEVChargingNeedsRequest.json | 449 ++++++++++ .../fixtures/calls/DataTransferRequest.json | 40 + .../calls/Get15118EVCertificateRequest.json | 68 ++ .../calls/GetCertificateStatusRequest.json | 80 ++ .../GetInstalledCertificateIdsRequest.json | 49 ++ .../calls/NotifyEVChargingNeedsRequest.json | 449 ++++++++++ .../fixtures/calls/NotifyReportRequest.json | 260 ++++++ .../tests/fixtures/calls/ResetRequest.json | 48 ++ .../datatypes/ChangeAvailabilityRequest.json | 69 ++ .../GetCertificateStatusRequest.json | 80 ++ .../NotifyEVChargingNeedsRequest.json | 449 ++++++++++ .../enums/NotifyEVChargingNeedsRequest.json | 449 ++++++++++ .../NotifyEVChargingNeedsRequest.py | 15 + .../golden_files/calls/DataTransferRequest.py | 14 + .../calls/Get15118EVCertificateRequest.py | 20 + .../calls/GetCertificateStatusRequest.py | 13 + .../GetInstalledCertificateIdsRequest.py | 16 + .../calls/NotifyEVChargingNeedsRequest.py | 15 + .../golden_files/calls/NotifyReportRequest.py | 17 + .../tests/golden_files/calls/ResetRequest.py | 15 + .../datatypes/ChangeAvailabilityRequest.py | 15 + .../datatypes/GetCertificateStatusRequest.py | 22 + .../datatypes/NotifyEVChargingNeedsRequest.py | 125 +++ .../enums/NotifyEVChargingNeedsRequest.py | 38 + scripts/v21/tests/test_code_generator.py | 98 +++ 38 files changed, 4325 insertions(+), 213 deletions(-) create mode 100644 ocpp/v21/__init__.py create mode 100644 scripts/v21/code_generator.py create mode 100755 scripts/v21/generate_implementation.py create mode 100644 scripts/v21/json_schema_parser.py create mode 100644 scripts/v21/tests/__init__.py create mode 100644 scripts/v21/tests/fixtures/call_results/NotifyEVChargingNeedsRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/DataTransferRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/Get15118EVCertificateRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/GetCertificateStatusRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/GetInstalledCertificateIdsRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/NotifyEVChargingNeedsRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/NotifyReportRequest.json create mode 100644 scripts/v21/tests/fixtures/calls/ResetRequest.json create mode 100644 scripts/v21/tests/fixtures/datatypes/ChangeAvailabilityRequest.json create mode 100644 scripts/v21/tests/fixtures/datatypes/GetCertificateStatusRequest.json create mode 100644 scripts/v21/tests/fixtures/datatypes/NotifyEVChargingNeedsRequest.json create mode 100644 scripts/v21/tests/fixtures/enums/NotifyEVChargingNeedsRequest.json create mode 100644 scripts/v21/tests/golden_files/call_results/NotifyEVChargingNeedsRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/DataTransferRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/Get15118EVCertificateRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/GetCertificateStatusRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/GetInstalledCertificateIdsRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/NotifyEVChargingNeedsRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/NotifyReportRequest.py create mode 100644 scripts/v21/tests/golden_files/calls/ResetRequest.py create mode 100644 scripts/v21/tests/golden_files/datatypes/ChangeAvailabilityRequest.py create mode 100644 scripts/v21/tests/golden_files/datatypes/GetCertificateStatusRequest.py create mode 100644 scripts/v21/tests/golden_files/datatypes/NotifyEVChargingNeedsRequest.py create mode 100644 scripts/v21/tests/golden_files/enums/NotifyEVChargingNeedsRequest.py create mode 100644 scripts/v21/tests/test_code_generator.py diff --git a/.github/actions/setup-python-build-env/action.yml b/.github/actions/setup-python-build-env/action.yml index 7087467e6..6b4101e02 100644 --- a/.github/actions/setup-python-build-env/action.yml +++ b/.github/actions/setup-python-build-env/action.yml @@ -8,4 +8,4 @@ runs: shell: bash run: | pip install --upgrade pip - pip install --user "poetry==1.1.11" + pip install --user "poetry==1.5.1" diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml index 4eba72349..f9182ec8c 100644 --- a/.github/workflows/publish-to-pypi.yml +++ b/.github/workflows/publish-to-pypi.yml @@ -25,7 +25,7 @@ jobs: - name: Code Quality Check shell: bash - run: make install && make tests + run: make install && make tests && make tests-format - name: Poetry bump version, build and publish shell: bash diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 4a69f851a..d6a9d9f20 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -28,4 +28,4 @@ jobs: - name: Code Quality Check shell: bash run: | - make install && make tests + make install && make tests && make tests-scripts diff --git a/.gitignore b/.gitignore index 168585dc3..cbcf8ffba 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +# OCA doesn't allow to publish the schema's while draft hasn't been published. +ocpp/v21/schemas # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] @@ -110,3 +112,5 @@ venv.bak/ ### OSX ### *.DS_Store + + diff --git a/Makefile b/Makefile index d41951ec8..22d0102f1 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: help .check-pypi-envs .install-poetry update install docs tests build deploy +.PHONY: help .install-poetry build deploy docs format format-scripts install update tests tests-scripts .DEFAULT_GOAL := help @@ -39,12 +39,20 @@ docs: .install-poetry format: .install-poetry poetry run isort ocpp tests && poetry run black ocpp tests +format-scripts: .install-poetry + poetry run isort --skip scripts/v21/tests/golden_files scripts/v21 && poetry run black --exclude scripts/v21/tests/golden_files scripts/v21/ + tests: .install-poetry poetry run black --check --diff ocpp tests poetry run isort --check-only ocpp tests poetry run flake8 ocpp tests poetry run py.test -vvv --cov=ocpp --cov-report=term-missing tests/ +tests-scripts: .install-poetry + poetry run black --check --diff --exclude scripts/v21/tests/golden_files scripts/v21/ + poetry run isort --check-only --skip scripts/v21/tests/golden_files scripts/v21 + poetry run pytest -vvv scripts/v21/tests + build: .install-poetry poetry build diff --git a/README.rst b/README.rst index 1a7662234..d3fc67199 100644 --- a/README.rst +++ b/README.rst @@ -14,6 +14,8 @@ Python package implementing the JSON version of the Open Charge Point Protocol (OCPP). Currently OCPP 1.6 (errata v4), OCPP 2.0 and OCPP 2.0.1 (Final Version) are supported. +This package has preliminary support for OCPP 2.1. See `OCPP 2.1 support`_ for more information. + You can find the documentation on `rtd`_. Installation @@ -190,6 +192,29 @@ To lower the logs for this package only use the following code: logging.getLogger('ocpp').setLevel(level=logging.DEBUG) logging.getLogger('ocpp').addHandler(logging.StreamHandler()) + +OCPP 2.1 support +---------------- + +The Open Charge Alliance (OCA) is working on OCPP 2.1. Only members of the OCA have +access to the `draft specification`_ and the corresponding `JSON schemas`_. + +Support for OCPP 2.1 Draft 1, v0.40, 2023-05-11 is located at `ocpp/v21`_. +Unfortunately, the OCA does not allow to include the JSON Schemas at this moment. + +The code in `ocpp/v21`_ is generated through a script. To generate the code yourself, run: + + +.. code-block:: shell + + $ poetry run python scripts/v21/generate_implementation.py --output=ocpp/v21 + +To execute the tests covering this script: + +.. code-block:: shell + + $ python run pytest scripts/v21/tests + License ------- @@ -204,3 +229,6 @@ Attribution-NoDerivatives 4.0 International Public License. .. _rtd: https://ocpp.readthedocs.io/en/latest/index.html .. _The Mobility House: https://www.mobilityhouse.com/int_en/ .. _websockets: https://pypi.org/project/websockets/ +.. _ocpp/v21: ocpp/v21 +.. _draft specification: https://oca.causewaynow.com/wg/OCA-V2X/document/743 +.. _JSON schemas: https://oca.causewaynow.com/wg/OCA-V2X/document/874 diff --git a/ocpp/v21/__init__.py b/ocpp/v21/__init__.py new file mode 100644 index 000000000..d6f6d6280 --- /dev/null +++ b/ocpp/v21/__init__.py @@ -0,0 +1,8 @@ +from ocpp.charge_point import ChargePoint as cp +from ocpp.v21 import call, call_result + + +class ChargePoint(cp): + _call = call + _call_result = call_result + _ocpp_version = "2.1" diff --git a/poetry.lock b/poetry.lock index 2374d7c58..cbeea89f1 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,10 +1,16 @@ +# This file is automatically @generated by Poetry 1.4.1 and should not be changed by hand. + [[package]] name = "alabaster" -version = "0.7.12" +version = "0.7.13" description = "A configurable sidebar-enabled Sphinx theme" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" +files = [ + {file = "alabaster-0.7.13-py3-none-any.whl", hash = "sha256:1ee19aca801bbabb5ba3f5f258e4422dfa86f82f3e9cefb0859b283cdd7f62a3"}, + {file = "alabaster-0.7.13.tar.gz", hash = "sha256:a27a4a084d5e690e16e01e03ad2b2e552c61a65469419b907243193de1a84ae2"}, +] [[package]] name = "asynctest" @@ -13,33 +19,47 @@ description = "Enhance the standard unittest package with features for testing a category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, + {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, +] [[package]] name = "attrs" -version = "22.2.0" +version = "23.1.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, + {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, +] + +[package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] -cov = ["attrs", "coverage-enable-subprocess", "coverage[toml] (>=5.3)"] -dev = ["attrs"] -docs = ["furo", "sphinx", "myst-parser", "zope.interface", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier"] -tests = ["attrs", "zope.interface"] -tests-no-zope = ["hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist", "cloudpickle", "mypy (>=0.971,<0.990)", "pytest-mypy-plugins"] -tests_no_zope = ["hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist", "cloudpickle", "mypy (>=0.971,<0.990)", "pytest-mypy-plugins"] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[docs,tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] [[package]] name = "babel" -version = "2.11.0" +version = "2.12.1" description = "Internationalization utilities" category = "dev" optional = false -python-versions = ">=3.6" +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"}, +] [package.dependencies] -pytz = ">=2015.7" +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} [[package]] name = "black" @@ -48,6 +68,20 @@ description = "The uncompromising code formatter." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, + {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, + {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, + {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, + {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, + {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, + {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, + {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, + {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, + {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, + {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, + {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, +] [package.dependencies] click = ">=8.0.0" @@ -66,30 +100,112 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2022.12.7" +version = "2023.5.7" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "certifi-2023.5.7-py3-none-any.whl", hash = "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"}, + {file = "certifi-2023.5.7.tar.gz", hash = "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7"}, +] [[package]] name = "charset-normalizer" -version = "2.1.1" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false -python-versions = ">=3.6.0" - -[package.extras] -unicode_backport = ["unicodedata2"] +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"}, +] [[package]] name = "click" -version = "8.1.3" +version = "8.1.6" description = "Composable command line interface toolkit" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "click-8.1.6-py3-none-any.whl", hash = "sha256:fa244bb30b3b5ee2cae3da8f55c9e5e0c0e86093306301fb418eb9dc40fbded5"}, + {file = "click-8.1.6.tar.gz", hash = "sha256:48ee849951919527a045bfe3bf7baa8a959c423134e1a5b98c05c20ba75a1cbd"}, +] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} @@ -102,14 +218,80 @@ description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +files = [ + {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, + {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, +] [[package]] name = "coverage" -version = "7.0.0" +version = "7.2.7" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "coverage-7.2.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d39b5b4f2a66ccae8b7263ac3c8170994b65266797fb96cbbfd3fb5b23921db8"}, + {file = "coverage-7.2.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6d040ef7c9859bb11dfeb056ff5b3872436e3b5e401817d87a31e1750b9ae2fb"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba90a9563ba44a72fda2e85302c3abc71c5589cea608ca16c22b9804262aaeb6"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d9405291c6928619403db1d10bd07888888ec1abcbd9748fdaa971d7d661b2"}, + {file = "coverage-7.2.7-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31563e97dae5598556600466ad9beea39fb04e0229e61c12eaa206e0aa202063"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ebba1cd308ef115925421d3e6a586e655ca5a77b5bf41e02eb0e4562a111f2d1"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cb017fd1b2603ef59e374ba2063f593abe0fc45f2ad9abdde5b4d83bd922a353"}, + {file = "coverage-7.2.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62a5c7dad11015c66fbb9d881bc4caa5b12f16292f857842d9d1871595f4495"}, + {file = "coverage-7.2.7-cp310-cp310-win32.whl", hash = "sha256:ee57190f24fba796e36bb6d3aa8a8783c643d8fa9760c89f7a98ab5455fbf818"}, + {file = "coverage-7.2.7-cp310-cp310-win_amd64.whl", hash = "sha256:f75f7168ab25dd93110c8a8117a22450c19976afbc44234cbf71481094c1b850"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:06a9a2be0b5b576c3f18f1a241f0473575c4a26021b52b2a85263a00f034d51f"}, + {file = "coverage-7.2.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5baa06420f837184130752b7c5ea0808762083bf3487b5038d68b012e5937dbe"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdec9e8cbf13a5bf63290fc6013d216a4c7232efb51548594ca3631a7f13c3a3"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52edc1a60c0d34afa421c9c37078817b2e67a392cab17d97283b64c5833f427f"}, + {file = "coverage-7.2.7-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63426706118b7f5cf6bb6c895dc215d8a418d5952544042c8a2d9fe87fcf09cb"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:afb17f84d56068a7c29f5fa37bfd38d5aba69e3304af08ee94da8ed5b0865833"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:48c19d2159d433ccc99e729ceae7d5293fbffa0bdb94952d3579983d1c8c9d97"}, + {file = "coverage-7.2.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0e1f928eaf5469c11e886fe0885ad2bf1ec606434e79842a879277895a50942a"}, + {file = "coverage-7.2.7-cp311-cp311-win32.whl", hash = "sha256:33d6d3ea29d5b3a1a632b3c4e4f4ecae24ef170b0b9ee493883f2df10039959a"}, + {file = "coverage-7.2.7-cp311-cp311-win_amd64.whl", hash = "sha256:5b7540161790b2f28143191f5f8ec02fb132660ff175b7747b95dcb77ac26562"}, + {file = "coverage-7.2.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f2f67fe12b22cd130d34d0ef79206061bfb5eda52feb6ce0dba0644e20a03cf4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a342242fe22407f3c17f4b499276a02b01e80f861f1682ad1d95b04018e0c0d4"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:171717c7cb6b453aebac9a2ef603699da237f341b38eebfee9be75d27dc38e01"}, + {file = "coverage-7.2.7-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49969a9f7ffa086d973d91cec8d2e31080436ef0fb4a359cae927e742abfaaa6"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b46517c02ccd08092f4fa99f24c3b83d8f92f739b4657b0f146246a0ca6a831d"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:a3d33a6b3eae87ceaefa91ffdc130b5e8536182cd6dfdbfc1aa56b46ff8c86de"}, + {file = "coverage-7.2.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:976b9c42fb2a43ebf304fa7d4a310e5f16cc99992f33eced91ef6f908bd8f33d"}, + {file = "coverage-7.2.7-cp312-cp312-win32.whl", hash = "sha256:8de8bb0e5ad103888d65abef8bca41ab93721647590a3f740100cd65c3b00511"}, + {file = "coverage-7.2.7-cp312-cp312-win_amd64.whl", hash = "sha256:9e31cb64d7de6b6f09702bb27c02d1904b3aebfca610c12772452c4e6c21a0d3"}, + {file = "coverage-7.2.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:58c2ccc2f00ecb51253cbe5d8d7122a34590fac9646a960d1430d5b15321d95f"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d22656368f0e6189e24722214ed8d66b8022db19d182927b9a248a2a8a2f67eb"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a895fcc7b15c3fc72beb43cdcbdf0ddb7d2ebc959edac9cef390b0d14f39f8a9"}, + {file = "coverage-7.2.7-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84606b74eb7de6ff581a7915e2dab7a28a0517fbe1c9239eb227e1354064dcd"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:0a5f9e1dbd7fbe30196578ca36f3fba75376fb99888c395c5880b355e2875f8a"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:419bfd2caae268623dd469eff96d510a920c90928b60f2073d79f8fe2bbc5959"}, + {file = "coverage-7.2.7-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:2aee274c46590717f38ae5e4650988d1af340fe06167546cc32fe2f58ed05b02"}, + {file = "coverage-7.2.7-cp37-cp37m-win32.whl", hash = "sha256:61b9a528fb348373c433e8966535074b802c7a5d7f23c4f421e6c6e2f1697a6f"}, + {file = "coverage-7.2.7-cp37-cp37m-win_amd64.whl", hash = "sha256:b1c546aca0ca4d028901d825015dc8e4d56aac4b541877690eb76490f1dc8ed0"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:54b896376ab563bd38453cecb813c295cf347cf5906e8b41d340b0321a5433e5"}, + {file = "coverage-7.2.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3d376df58cc111dc8e21e3b6e24606b5bb5dee6024f46a5abca99124b2229ef5"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e330fc79bd7207e46c7d7fd2bb4af2963f5f635703925543a70b99574b0fea9"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e9d683426464e4a252bf70c3498756055016f99ddaec3774bf368e76bbe02b6"}, + {file = "coverage-7.2.7-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d13c64ee2d33eccf7437961b6ea7ad8673e2be040b4f7fd4fd4d4d28d9ccb1e"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b7aa5f8a41217360e600da646004f878250a0d6738bcdc11a0a39928d7dc2050"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8fa03bce9bfbeeef9f3b160a8bed39a221d82308b4152b27d82d8daa7041fee5"}, + {file = "coverage-7.2.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:245167dd26180ab4c91d5e1496a30be4cd721a5cf2abf52974f965f10f11419f"}, + {file = "coverage-7.2.7-cp38-cp38-win32.whl", hash = "sha256:d2c2db7fd82e9b72937969bceac4d6ca89660db0a0967614ce2481e81a0b771e"}, + {file = "coverage-7.2.7-cp38-cp38-win_amd64.whl", hash = "sha256:2e07b54284e381531c87f785f613b833569c14ecacdcb85d56b25c4622c16c3c"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:537891ae8ce59ef63d0123f7ac9e2ae0fc8b72c7ccbe5296fec45fd68967b6c9"}, + {file = "coverage-7.2.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06fb182e69f33f6cd1d39a6c597294cff3143554b64b9825d1dc69d18cc2fff2"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:201e7389591af40950a6480bd9edfa8ed04346ff80002cec1a66cac4549c1ad7"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6951407391b639504e3b3be51b7ba5f3528adbf1a8ac3302b687ecababf929e"}, + {file = "coverage-7.2.7-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f48351d66575f535669306aa7d6d6f71bc43372473b54a832222803eb956fd1"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b29019c76039dc3c0fd815c41392a044ce555d9bcdd38b0fb60fb4cd8e475ba9"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:81c13a1fc7468c40f13420732805a4c38a105d89848b7c10af65a90beff25250"}, + {file = "coverage-7.2.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:975d70ab7e3c80a3fe86001d8751f6778905ec723f5b110aed1e450da9d4b7f2"}, + {file = "coverage-7.2.7-cp39-cp39-win32.whl", hash = "sha256:7ee7d9d4822c8acc74a5e26c50604dff824710bc8de424904c0982e25c39c6cb"}, + {file = "coverage-7.2.7-cp39-cp39-win_amd64.whl", hash = "sha256:eb393e5ebc85245347950143969b241d08b52b88a3dc39479822e073a1a8eb27"}, + {file = "coverage-7.2.7-pp37.pp38.pp39-none-any.whl", hash = "sha256:b7b4c971f05e6ae490fef852c218b0e79d4e52f79ef0c8475566584a8fb3e01d"}, + {file = "coverage-7.2.7.tar.gz", hash = "sha256:924d94291ca674905fe9481f12294eb11f2d3d3fd1adb20314ba89e94f44ed59"}, +] [package.dependencies] tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} @@ -124,14 +306,22 @@ description = "Docutils -- Python Documentation Utilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, + {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, +] [[package]] name = "exceptiongroup" -version = "1.1.0" +version = "1.1.2" description = "Backport of PEP 654 (exception groups)" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, + {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, +] [package.extras] test = ["pytest (>=6)"] @@ -143,6 +333,10 @@ description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false python-versions = ">=3.6.1" +files = [ + {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, + {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, +] [package.dependencies] importlib-metadata = {version = ">=1.1.0,<4.3", markers = "python_version < \"3.8\""} @@ -157,6 +351,10 @@ description = "Internationalized Domain Names in Applications (IDNA)" category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, + {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, +] [[package]] name = "imagesize" @@ -165,6 +363,10 @@ description = "Getting image size from png/jpeg/jpeg2000/gif file" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "imagesize-1.4.1-py2.py3-none-any.whl", hash = "sha256:0d8d18d08f840c19d0ee7ca1fd82490fdc3729b7ac93f49870406ddde8ef8d8b"}, + {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, +] [[package]] name = "importlib-metadata" @@ -173,51 +375,67 @@ description = "Read metadata from Python packages" category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, + {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, +] [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] [[package]] name = "importlib-resources" -version = "5.10.1" +version = "5.12.0" description = "Read resources from Python packages" category = "main" optional = false python-versions = ">=3.7" +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"}, +] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"] +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)"] [[package]] name = "iniconfig" -version = "1.1.1" -description = "iniconfig: brain-dead simple config-ini parsing" +version = "2.0.0" +description = "brain-dead simple config-ini parsing" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" +files = [ + {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, + {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, +] [[package]] name = "isort" -version = "5.11.4" +version = "5.11.5" description = "A Python utility / library to sort Python imports." category = "dev" optional = false python-versions = ">=3.7.0" +files = [ + {file = "isort-5.11.5-py3-none-any.whl", hash = "sha256:ba1d72fb2595a01c7895a5128f9585a5cc4b6d395f1c8d514989b9a7eb2a8746"}, + {file = "isort-5.11.5.tar.gz", hash = "sha256:6be1f76a507cb2ecf16c7cf14a37e41609ca082330be4e3436a18ef74add55db"}, +] [package.extras] colors = ["colorama (>=0.4.3,<0.5.0)"] -requirements-deprecated-finder = ["pip-api", "pipreqs"] -pipfile-deprecated-finder = ["pipreqs", "requirementslib"] +pipfile-deprecated-finder = ["pip-shims (>=0.5.2)", "pipreqs", "requirementslib"] plugins = ["setuptools"] +requirements-deprecated-finder = ["pip-api", "pipreqs"] [[package]] name = "jinja2" @@ -226,6 +444,10 @@ description = "A very fast and expressive template engine." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] [package.dependencies] MarkupSafe = ">=2.0" @@ -240,6 +462,10 @@ description = "An implementation of JSON Schema validation for Python" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "jsonschema-4.17.3-py3-none-any.whl", hash = "sha256:a870ad254da1a8ca84b6a2905cac29d265f805acc57af304784962a2aa6508f6"}, + {file = "jsonschema-4.17.3.tar.gz", hash = "sha256:0f864437ab8b6076ba6707453ef8f98a6a0d512a80e93f8abdb676f737ecb60d"}, +] [package.dependencies] attrs = ">=17.4.0" @@ -255,11 +481,63 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339- [[package]] name = "markupsafe" -version = "2.1.1" +version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, +] [[package]] name = "mccabe" @@ -268,30 +546,46 @@ description = "McCabe checker, plugin for flake8" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, + {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, +] [[package]] name = "mypy-extensions" -version = "0.4.3" -description = "Experimental type system extensions for programs checked with the mypy typechecker." +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] [[package]] name = "packaging" -version = "22.0" +version = "23.1" description = "Core utilities for Python packages" category = "dev" 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"}, +] [[package]] name = "pathspec" -version = "0.10.3" +version = "0.11.1" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] [[package]] name = "pkgutil-resolve-name" @@ -300,26 +594,41 @@ description = "Resolve a name to an object." category = "main" optional = false python-versions = ">=3.6" +files = [ + {file = "pkgutil_resolve_name-1.3.10-py3-none-any.whl", hash = "sha256:ca27cc078d25c5ad71a9de0a7a330146c4e014c2462d9af19c6b828280649c5e"}, + {file = "pkgutil_resolve_name-1.3.10.tar.gz", hash = "sha256:357d6c9e6a755653cfd78893817c0853af365dd51ec97f3d358a819373bbd174"}, +] [[package]] name = "platformdirs" -version = "2.6.0" +version = "3.9.1" 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 = [ + {file = "platformdirs-3.9.1-py3-none-any.whl", hash = "sha256:ad8291ae0ae5072f66c16945166cb11c63394c7a3ad1b1bc9828ca3162da8c2f"}, + {file = "platformdirs-3.9.1.tar.gz", hash = "sha256:1b42b450ad933e981d56e59f1b97495428c9bd60698baab9f3eb3d00d5822421"}, +] + +[package.dependencies] +typing-extensions = {version = ">=4.6.3", markers = "python_version < \"3.8\""} [package.extras] -docs = ["furo (>=2022.9.29)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.19.4)", "sphinx (>=5.3)"] -test = ["appdirs (==1.4.4)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest (>=7.2)"] +docs = ["furo (>=2023.5.20)", "proselint (>=0.13)", "sphinx (>=7.0.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4.1)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" -version = "1.0.0" +version = "1.2.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, + {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, +] [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} @@ -335,6 +644,10 @@ description = "Python style guide checker" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, + {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, +] [[package]] name = "pyflakes" @@ -343,36 +656,76 @@ description = "passive checker of Python programs" category = "dev" optional = false python-versions = ">=3.6" +files = [ + {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, + {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, +] [[package]] name = "pygments" -version = "2.13.0" +version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, + {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, +] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pyrsistent" -version = "0.19.2" +version = "0.19.3" description = "Persistent/Functional/Immutable data structures" category = "main" 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.2.0" +version = "7.4.0" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-7.4.0-py3-none-any.whl", hash = "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32"}, + {file = "pytest-7.4.0.tar.gz", hash = "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a"}, +] [package.dependencies] -attrs = ">=19.2.0" 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\""} @@ -382,7 +735,7 @@ pluggy = ">=0.12,<2.0" tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} [package.extras] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] +testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] [[package]] name = "pytest-asyncio" @@ -391,6 +744,10 @@ description = "Pytest support for asyncio" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "pytest-asyncio-0.20.3.tar.gz", hash = "sha256:83cbf01169ce3e8eb71c6c278ccb0574d1a7a3bb8eaaf5e50e0ad342afb33b36"}, + {file = "pytest_asyncio-0.20.3-py3-none-any.whl", hash = "sha256:f129998b209d04fcc65c96fc85c11e5316738358909a8399e93be553d7656442"}, +] [package.dependencies] pytest = ">=6.1.0" @@ -398,48 +755,77 @@ typing-extensions = {version = ">=3.7.2", markers = "python_version < \"3.8\""} [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] -testing = ["coverage (>=6.2)", "hypothesis (>=5.7.1)", "flaky (>=3.5.0)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] +testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy (>=0.931)", "pytest-trio (>=0.7.0)"] [[package]] name = "pytest-cov" -version = "4.0.0" +version = "4.1.0" description = "Pytest plugin for measuring coverage." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" +files = [ + {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, + {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, +] [package.dependencies] coverage = {version = ">=5.2.1", extras = ["toml"]} pytest = ">=4.6" [package.extras] -testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pytz" -version = "2022.7" +version = "2023.3" description = "World timezone definitions, modern and historical" category = "dev" optional = false python-versions = "*" +files = [ + {file = "pytz-2023.3-py2.py3-none-any.whl", hash = "sha256:a151b3abb88eda1d4e34a9814df37de2a80e301e68ba0fd856fb9b46bfbbbffb"}, + {file = "pytz-2023.3.tar.gz", hash = "sha256:1d8ce29db189191fb55338ee6d0387d82ab59f3d00eac103412d64e0ebd0c588"}, +] [[package]] name = "requests" -version = "2.28.1" +version = "2.31.0" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" +files = [ + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, +] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<3" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "setuptools" +version = "68.0.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-68.0.0-py3-none-any.whl", hash = "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f"}, + {file = "setuptools-68.0.0.tar.gz", hash = "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235"}, +] + +[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"] [[package]] name = "snowballstemmer" @@ -448,6 +834,10 @@ description = "This package provides 29 stemmers for 28 languages generated from category = "dev" optional = false python-versions = "*" +files = [ + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, +] [[package]] name = "sphinx" @@ -456,6 +846,10 @@ description = "Python documentation generator" category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "Sphinx-2.4.5-py3-none-any.whl", hash = "sha256:02d7e9dc5f30caa42a682b26de408b755a55c7b07f356a30a3b6300bf7d4740e"}, + {file = "Sphinx-2.4.5.tar.gz", hash = "sha256:b00394e90463e7482c4cf59e7db1c8604baeca1468abfc062904dedc1cea6fcc"}, +] [package.dependencies] alabaster = ">=0.7,<0.8" @@ -467,6 +861,7 @@ Jinja2 = ">=2.3" packaging = "*" Pygments = ">=2.0" requests = ">=2.5.0" +setuptools = "*" snowballstemmer = ">=1.1" sphinxcontrib-applehelp = "*" sphinxcontrib-devhelp = "*" @@ -477,7 +872,7 @@ sphinxcontrib-serializinghtml = "*" [package.extras] docs = ["sphinxcontrib-websupport"] -test = ["pytest (<5.3.3)", "pytest-cov", "html5lib", "flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.761)", "docutils-stubs"] +test = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-import-order", "html5lib", "mypy (>=0.761)", "pytest (<5.3.3)", "pytest-cov"] [[package]] name = "sphinxcontrib-applehelp" @@ -486,9 +881,13 @@ description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple category = "dev" optional = false python-versions = ">=3.5" +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"}, +] [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -498,9 +897,13 @@ description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, + {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, +] [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -510,10 +913,14 @@ description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML h category = "dev" optional = false python-versions = ">=3.6" +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"}, +] [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +lint = ["docutils-stubs", "flake8", "mypy"] +test = ["html5lib", "pytest"] [[package]] name = "sphinxcontrib-jsmath" @@ -522,9 +929,13 @@ description = "A sphinx extension which renders display math in HTML via JavaScr category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, + {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, +] [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["flake8", "mypy", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -533,9 +944,13 @@ description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp d category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, + {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, +] [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -545,9 +960,13 @@ description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs category = "dev" optional = false python-versions = ">=3.5" +files = [ + {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, + {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, +] [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] +lint = ["docutils-stubs", "flake8", "mypy"] test = ["pytest"] [[package]] @@ -557,178 +976,109 @@ description = "A lil' TOML parser" category = "dev" optional = false python-versions = ">=3.7" +files = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] [[package]] name = "typed-ast" -version = "1.5.4" +version = "1.5.5" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" 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 = "typing-extensions" -version = "4.4.0" +version = "4.7.1" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" +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"}, +] [[package]] name = "urllib3" -version = "1.26.13" +version = "2.0.4" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +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"}, +] [package.extras] -brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] -secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] -socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "zipp" -version = "3.11.0" +version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.7" +files = [ + {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, + {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, +] [package.extras] -docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "jaraco.functools", "more-itertools", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "pytest-flake8"] +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)"] [metadata] -lock-version = "1.1" +lock-version = "2.0" python-versions = "^3.7" -content-hash = "1faa57c1b0477263f6f1766bbb1559aed9a250182754f3d735bf65abba91a0a1" - -[metadata.files] -alabaster = [ - {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, - {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, -] -asynctest = [ - {file = "asynctest-0.13.0-py3-none-any.whl", hash = "sha256:5da6118a7e6d6b54d83a8f7197769d046922a44d2a99c21382f0a6e4fadae676"}, - {file = "asynctest-0.13.0.tar.gz", hash = "sha256:c27862842d15d83e6a34eb0b2866c323880eb3a75e4485b079ea11748fd77fac"}, -] -attrs = [] -babel = [] -black = [] -certifi = [] -charset-normalizer = [] -click = [ - {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, - {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, -] -colorama = [] -coverage = [] -docutils = [ - {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"}, - {file = "docutils-0.17.1.tar.gz", hash = "sha256:686577d2e4c32380bb50cbb22f575ed742d58168cee37e99117a854bcd88f125"}, -] -exceptiongroup = [] -flake8 = [] -idna = [] -imagesize = [] -importlib-metadata = [ - {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, - {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, -] -importlib-resources = [] -iniconfig = [ - {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, - {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, -] -isort = [] -jinja2 = [] -jsonschema = [] -markupsafe = [] -mccabe = [] -mypy-extensions = [ - {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, - {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, -] -packaging = [] -pathspec = [] -pkgutil-resolve-name = [] -platformdirs = [] -pluggy = [ - {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, - {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, -] -pycodestyle = [] -pyflakes = [] -pygments = [] -pyrsistent = [] -pytest = [] -pytest-asyncio = [] -pytest-cov = [] -pytz = [] -requests = [] -snowballstemmer = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, -] -sphinx = [ - {file = "Sphinx-2.4.5-py3-none-any.whl", hash = "sha256:02d7e9dc5f30caa42a682b26de408b755a55c7b07f356a30a3b6300bf7d4740e"}, - {file = "Sphinx-2.4.5.tar.gz", hash = "sha256:b00394e90463e7482c4cf59e7db1c8604baeca1468abfc062904dedc1cea6fcc"}, -] -sphinxcontrib-applehelp = [ - {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"}, -] -sphinxcontrib-devhelp = [ - {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"}, - {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"}, -] -sphinxcontrib-htmlhelp = [ - {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"}, -] -sphinxcontrib-jsmath = [ - {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"}, - {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"}, -] -sphinxcontrib-qthelp = [ - {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"}, - {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"}, -] -sphinxcontrib-serializinghtml = [ - {file = "sphinxcontrib-serializinghtml-1.1.5.tar.gz", hash = "sha256:aa5f6de5dfdf809ef505c4895e51ef5c9eac17d0f287933eb49ec495280b6952"}, - {file = "sphinxcontrib_serializinghtml-1.1.5-py2.py3-none-any.whl", hash = "sha256:352a9a00ae864471d3a7ead8d7d79f5fc0b57e8b3f95e9867eb9eb28999b92fd"}, -] -tomli = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] -typed-ast = [ - {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, - {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, - {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, - {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, - {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, - {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, - {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, - {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, - {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, - {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, - {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, - {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, - {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, - {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, - {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, - {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, - {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, -] -typing-extensions = [] -urllib3 = [] -zipp = [] +content-hash = "0983f70c2ac974cee75c53b76ecd91de89a609577937acd69ad841ec9f299b79" diff --git a/pyproject.toml b/pyproject.toml index f48fbcd18..e2e5bb175 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,6 +43,7 @@ sphinx = "^2.4.5" black = "^22" isort = "^5" flake8 = "^5" +click = "^8.1.6" [tool.black] line-length = 88 diff --git a/scripts/v21/code_generator.py b/scripts/v21/code_generator.py new file mode 100644 index 000000000..e66e58a66 --- /dev/null +++ b/scripts/v21/code_generator.py @@ -0,0 +1,411 @@ +import re + +from json_schema_parser import ( + NOT_SET, + Any, + Array, + Boolean, + Enum, + Integer, + Number, + Object, + String, +) + +# Tab's are 4 spaces. +TAB = " " + + +def generate_calls(calls: list[Object]) -> str: + """Generate the dataclasses that represent all OCPP 2.1 Calls.""" + return _generate_messages(calls) + + +def generate_call_results(call_results: list[Object]) -> str: + """Generate the dataclasses that represent all OCPP 2.1 CallResults.""" + return _generate_messages(call_results) + + +def generate_enums(messages: list[Object]) -> str: + """Generate all Enum that `messages` rely on directly or indirectly.""" + dependencies = _all_dependencies(messages) + enums = [] + + for dependency in dependencies: + if not isinstance(dependency, Enum): + continue + + if dependency in enums: + continue + + enums.append(dependency) + + # Order enums alphabetically + enums = sorted(enums, key=lambda x: x.name) + + code = """try: + # breaking change introduced in python 3.11 + from enum import StrEnum +except ImportError: # pragma: no cover + from enum import Enum # pragma: no cover + + class StrEnum(str, Enum): # pragma: no cover + pass # pragma: no cover""" + for enum in enums: + lines = _generate_code_for_enum(enum) + code += "\n\n\n" + lines + code += "\n" + + return code + + +def generate_datatypes(messages: list[Object]) -> str: + """Generate all custom types that OCPP 2.1 Calls and CallResults rely on.""" + objects = [] + dependencies = _all_dependencies(messages) + + for dependency in dependencies: + if not isinstance(dependency, Object): + continue + + if dependency in objects: + continue + + objects.append(dependency) + + objects = sorted(objects, key=lambda x: x.name) + + code = "" + code += "from __future__ import annotations\n" + code += "from typing import List, Optional\n" + code += "from dataclasses import dataclass\n" + imports = _generate_import_statement_for_external_enums(objects) + if imports != "": + code += "\n" + code += imports + + for object in objects: + code += "\n\n" + _generate_code_for_object(object) + + return code + + +def _generate_messages(messages: list[Object]) -> str: + objects = sorted(messages, key=lambda x: x.name) + + code = "" + code += "from dataclasses import dataclass\n" + code += "from typing import Any, List, Optional\n" + imports = _generate_import_statement_for_external_datatypes(objects) + if imports != "": + code += "\n" + code += imports + + imports = _generate_import_statement_for_external_enums(objects) + if imports != "": + code += "\n" + code += imports + + for object in objects: + code += "\n\n" + _generate_code_for_object(object) + + return code + + +def _all_dependencies( + containers: list[Object | Array], +) -> list[Object | Enum | Array]: + """Return a list of `Object`s`, `Enum`s and `Array`s `containers` rely on.""" + visited_dependencies = [] + while True: + containers = _direct_dependencies(containers) + visited_dependencies += containers + + if len(containers) == 0: + break + + return visited_dependencies + + +def _direct_dependencies( + containers: list[Object | Array], +) -> list[Object | Enum | Array]: + """Return a list with all direct dependencies of `containers`. + + This function iterates over every `container`s properties. When a property is of type `Object` or `Enum`, these types + are included in the return value. `Array`s holding `Object`s or `Enum`s are included too. + + Different `Object`s might rely on the same dependency. Any duplicate dependencies are removed. + `Object`s relying on each other aren't in the return value either. + + """ + dependencies = [] + + for container in containers: + if isinstance(container, Array): + if ( + isinstance(container.type, Object) + or isinstance(container.type, Enum) + or isinstance(container.type, Array) + ): + dependencies.append(container.type) + continue + + if isinstance(container, Enum): + continue + + for property in container.properties.values(): + if ( + isinstance(property.type, Object) + or isinstance(property.type, Enum) + or isinstance(property.type, Array) + ): + dependencies.append(property.type) + return dependencies + + +def _generate_code_for_objects(objects: list[Object]) -> str: + # Order enums alphabetically + objects = sorted(objects, key=lambda x: x.name) + + code = "" + for object in objects: + code += "\n\n\n" + _generate_code_for_object(object) + + return code + + +def _generate_code_for_object(object: Object) -> str: + lines = [ + "@dataclass", + f"class {object.name}:", + ] + if len(object.properties) == 0: + lines.append(f"{TAB}pass") + + return "\n".join(lines) + + property_names = sorted( + object.properties.keys(), key=lambda x: _camel_to_snake_case(x) + ) + skipped_properties = [] + + for name in property_names: + property = object.properties[name] + if not property.required or property.has_default_value(): + skipped_properties.append(name) + continue + + lines.append( + f"{TAB}{_camel_to_snake_case(name)}: {_get_python_type(property.type)}" + ) + + for name in skipped_properties: + property = object.properties[name] + if isinstance(property.type, Any): + lines.append( + f"{TAB}{_camel_to_snake_case(name)}: {_get_python_type(property.type)} = None" + ) + continue + + if not property.has_default_value(): + lines.append( + f"{TAB}{_camel_to_snake_case(name)}: Optional[{_get_python_type(property.type)}] = None" + ) + continue + + default = property.default + if isinstance(property.type, Enum): + default = f"{property.type.name}.{_camel_to_snake_case(property.default)}" + if isinstance(property.type, String): + default = f'"{property.default}"' + + lines.append( + f"{TAB}{_camel_to_snake_case(name)}: {_get_python_type(property.type)} = {default}" + ) + + return "\n".join(lines) + "\n" + + +def _get_python_type(value) -> str: + """Return a Python type hint as a `str` for given object.""" + if isinstance(value, Any): + return "Any" + + if isinstance(value, String): + return "str" + + if isinstance(value, Integer): + return "int" + + if isinstance(value, Number): + return "float" + + if isinstance(value, Array): + value.type + return f"List[{_get_python_type(value.type)}]" + + if isinstance(value, Object): + return value.name + + if isinstance(value, Enum): + if _has_potential_naming_collision_with_a_message(value.name): + return f"enums.{value.name}" + return value.name + + if isinstance(value, Boolean): + return "bool" + else: + raise RuntimeError(value) + + +def _generate_code_for_enum(value: Enum) -> str: + lines = [ + f"class {value.name}(StrEnum):", + ] + + for variant in value.variants: + lines.append(f'{TAB}{_camel_to_snake_case(variant)} = "{variant}"') + + return "\n".join(lines) + + +def _generate_import_statement_for_external_enums(objects: list[Object]) -> str: + """Generate an import statement for all `Enum`s that the `objects` rely on.""" + enums = _external_enums_required(objects) + enums = sorted(enums, key=lambda x: x.name) + + code = "" + if len(enums) == 0: + return code + + for enum in enums: + if _has_potential_naming_collision_with_a_message(enum.name): + code += "import enums\n" + break + + enums = [ + enum + for enum in enums + if not _has_potential_naming_collision_with_a_message(enum.name) + ] + + if len(enums) == 0: + return code + + code += "from enums import (\n" + for enum in enums: + code += f"{TAB}{enum.name},\n" + code += ")\n" + + return code + + +def _generate_import_statement_for_external_datatypes(objects: list[Object]) -> str: + """Generate an import statement for all datatypes that the `objects` rely on.""" + objects = _external_datatypes_required(objects) + objects = sorted(objects, key=lambda x: x.name) + + if len(objects) == 0: + return "" + + code = "from datatypes import (\n" + for object in objects: + if object.name == "data" and len(object.properties) == 0: + continue + + code += f"{TAB}{object.name},\n" + code += ")\n" + + return code + + +def _external_enums_required(objects: list[Object]) -> list[Enum]: + """Return a list of all `Enum`s that `objects` depend on directly. + The list is sorted alphabetically and without duplicates. These enums are + not in scope of the `objects` and must be imported.""" + enums = [] + for dependency in _direct_dependencies(objects): + if isinstance(dependency, Array): + dependency = dependency.type + + if not isinstance(dependency, Enum): + continue + + if dependency in enums: + continue + + enums.append(dependency) + + return enums + + +def _external_datatypes_required(objects: list[Object]) -> list[Object]: + """Return a list of all `Object`s that `objects` depend on directly.""" + filtered_objects = [] + for dependency in _direct_dependencies(objects): + if isinstance(dependency, Array): + dependency = dependency.type + + if not isinstance(dependency, Object): + continue + + if dependency in objects: + continue + + if dependency in filtered_objects: + continue + + filtered_objects.append(dependency) + + return filtered_objects + + +def _has_potential_naming_collision_with_a_message(name: str) -> bool: + """A few request and responses rely on enums that collide with message names. + + For example, the Reset request relies on the Reset enum. Without handling these naming collisions, the code for the Reset request looks like this: + + from enums import ( + Reset, + ) + + class Reset(StrEnum): + type: Reset + + This is wrong. Instead, the code should look like this: + + import enums + + class Reset(StrEnum): + type: enums.Reset + + It's hard to find these exceptional cases dynamically. So I opted to hard code all + enums that share a name with a call or call result. + """ + return name in ["GetCertificateStatus", "TransactionEvent", "Reset"] + + +def _camel_to_snake_case(value: str) -> str: + # Make sure "_" is inserted between "iso" and "15118". + value = value.replace("iso15118", "iso_15118") + + # Prevent "V2X" from becoming "v2_x". + value = value.replace("V2X", "V2x") + + # Prevent "V2G" from becoming "v2_g". + value = value.replace("V2G", "V2g") + + # Prevent "prioritizedEMAIDs" from becoming "prioritized_emaid_s". + value = value.replace("EMAID", "Emaid") + + # quick for measurands like "CUrrent.Import" + value = value.replace(".", "") + + # This fix prevents "V2X" from becomming "v2_x". + + value = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", value) + value = re.sub("([a-z0-9])([A-Z])(?=\\S)", r"\1_\2", value).lower() + + # fix for s309-1_p-16a = "s309-1P-16A" + return value.replace("-", "_") diff --git a/scripts/v21/generate_implementation.py b/scripts/v21/generate_implementation.py new file mode 100755 index 000000000..a55749d6a --- /dev/null +++ b/scripts/v21/generate_implementation.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +""" This scripts consumes json schemas provided by the Open Charge Alliance +and produces Python code for all data types in these schemas. + +Each schema follows the following structure: + + { + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "type": { + "$ref": "#/definitions/ResetEnumType" + }, + }, + "required": [ + "type" + ], + "definitions": { + "CustomDataType": { + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ResetEnumType": { + "type": "string", + "additionalProperties": false, + "enum": [ + "Immediate", + "OnIdle" + ] + } + }, + ... + } + +First, this script iterates over each property in "properties" and resolve +the reference. Any references in the references (E.g. assume #/definitions/someObject also contains references) are resolved too. + +Per schema, an `Object` is created Below you'll see a simplified representation of the such `Object`: + + Object( + name="Reset", + properties={ + "type": Property( + type=Enum(...), + required=True, + ... + ), + "customData": Property( + type=Object(...), + required=False, + ... + ) + } + ) + + +Second, the list of `Object`s is passed to 4 code generators: + * `generate_calls()` + * `generate_call_results()` + * `generate_enums()` + * `generate_types()` + +Each generater returns a `str` with valid Python code. The generators remove duplicate objects and enums, sort items alphabetically, generate import statements etc. + +This Python code can be written to source files. +""" + +import json +import pathlib +import sys + +import click +from code_generator import ( + generate_call_results, + generate_calls, + generate_datatypes, + generate_enums, +) +from json_schema_parser import Object, parse + + +@click.command() +@click.argument("input", type=click.Path(exists=True)) +@click.option( + "--output", + help="folder for the generated code", + type=click.Path(file_okay=False, dir_okay=True, writable=True), +) +def main(input: str, output: str): + input: pathlib.Path = pathlib.Path(input) + output: pathlib.Path = pathlib.Path(output) + + if input.is_dir(): + schema_files = input.glob("*json") + elif input.is_file() and input.suffix == ".json": + schema_files = [input] + else: + sys.exit(1) + + calls: list[Object] = [] + call_results: list[Object] = [] + + for schema_file in schema_files: + schema = json.load(schema_file.open(encoding="utf-8-sig")) + + if schema["$id"].endswith("Response"): + name = schema["$id"].split(":")[-1][:-8] + object = parse(name, schema) + call_results.append(object) + if schema["$id"].endswith("Request"): + name = schema["$id"].split(":")[-1][:-7] + object = parse(name, schema) + calls.append(object) + + continue + + messages = calls + call_results + (output / "call.py").write_text(generate_calls(calls)) + (output / "call_result.py").write_text(generate_call_results(call_results)) + (output / "enums.py").write_text(generate_enums(messages)) + (output / "datatypes.py").write_text(generate_datatypes(messages)) + + +if __name__ == "__main__": + main() diff --git a/scripts/v21/json_schema_parser.py b/scripts/v21/json_schema_parser.py new file mode 100644 index 000000000..c1de811c6 --- /dev/null +++ b/scripts/v21/json_schema_parser.py @@ -0,0 +1,255 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +Schema = dict +SubSchema = dict + + +class _NOT_SET: + pass + + +NOT_SET = _NOT_SET() + + +def parse(name: str, schema: Schema) -> Object: + """Parse `schema` as an `Object`.""" + return _parse_as_object(name, schema, schema) + + +def _parse_as_object(name: str, parent: SubSchema, schema: Schema) -> Object: + properties = {} + for attribute, sub_schema in parent.get("properties", {}).items(): + if contains_reference(sub_schema): + sub_schema = resolve_reference(sub_schema["$ref"], schema) + + if sub_schema == {}: + type = Any() + elif is_object(sub_schema): + type = _parse_as_object(pascal_to_camel_case(attribute), sub_schema, schema) + elif is_string(sub_schema): + type = String.from_sub_schema(sub_schema) + elif is_enum(sub_schema): + type = Enum.from_sub_schema(sub_schema) + elif is_integer(sub_schema): + type = Integer.from_sub_schema(sub_schema) + elif is_boolean(sub_schema): + type = Boolean() + elif is_number(sub_schema): + type = Number.from_sub_schema(sub_schema) + elif is_array(sub_schema): + if contains_reference(sub_schema["items"]): + _name = ( + sub_schema["items"]["$ref"] + .split("/")[-1] + .removesuffix("Type") + .removesuffix("Enum") + ) + + sub_schema = resolve_reference(sub_schema["items"]["$ref"], schema) + if is_object(sub_schema): + type = _parse_as_object(_name, sub_schema, schema) + elif is_enum(sub_schema): + type = Enum.from_sub_schema(sub_schema) + else: + raise RuntimeError("HOOY") + elif is_string(sub_schema["items"]): + type = String.from_sub_schema(sub_schema["items"]) + elif is_integer(sub_schema["items"]): + type = Integer.from_sub_schema(sub_schema["items"]) + else: + raise RuntimeError( + f"No support for array of type {sub_schema['items']} in {attribute}" + ) + type = Array( + type=type, + min_items=sub_schema.get("minItems"), + ) + + else: + raise RuntimeError(f"No support for {sub_schema['type']} in {attribute}") + + # TODO: add support for default value sub_schema['default'] + properties[attribute] = Property( + type=type, + required=attribute in parent.get("required", []), + default=sub_schema.get("default", NOT_SET), + ) + + return Object(pascal_to_camel_case(name), properties) + + +@dataclass(frozen=True) +class Boolean: + pass + + +@dataclass(frozen=True) +class String: + max_length: int | None + + @staticmethod + def from_sub_schema(sub_schema: SubSchema) -> "String": + if sub_schema.get("type") != "string": + raise RuntimeError("sub schema is not a string") + + return String(sub_schema.get("maxLength")) + + +@dataclass(frozen=True) +class Enum: + """Class modelling the Enum in JSON schema.""" + + name: str + variants: list[str] + + @staticmethod + def from_sub_schema(schema: SubSchema) -> "Enum": + """Create `Enum` from a json subschema that defines an enum. + Such subschema looks like this: + + ``` + { + "javaType": "UpdateFirmwareStatusEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Accepted", + "Rejected", + "AcceptedCanceled", + "InvalidCertificate", + "RevokedCertificate" + ] + } + ``` + """ + return Enum(name=schema["javaType"][:-4], variants=schema["enum"]) + + +@dataclass(frozen=True) +class Object: + name: str + properties: dict[str, "Property"] + + +@dataclass(frozen=True) +class Integer: + minimum: int | None + maximum: int | None + + @staticmethod + def from_sub_schema(sub_schema: SubSchema) -> "Integer": + if sub_schema.get("type") != "integer": + raise RuntimeError("sub schema is not a string") + + return Integer(sub_schema.get("minimum"), sub_schema.get("maximum")) + + +@dataclass(frozen=True) +class Number: + minimum: int | None + maximum: int | None + + @staticmethod + def from_sub_schema(sub_schema: SubSchema) -> "Number": + if sub_schema.get("type") != "number": + raise RuntimeError("sub schema is not a string") + + return Number(sub_schema.get("minimum"), sub_schema.get("maximum")) + + +@dataclass(frozen=True) +class Array: + type: Object | Integer | String + min_items: int | None + + +@dataclass(frozen=True) +class Any: + pass + + +@dataclass(frozen=True) +class Property: + type: String | Object | Integer | Boolean | Array | Any + required: bool + default: Any = NOT_SET + + def has_default_value(self) -> bool: + return self.default is not NOT_SET + + +def contains_reference(sub_schema: SubSchema) -> bool: + return "$ref" in sub_schema.keys() + + +def is_object(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "object" + + +def is_string(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "string" and not "enum" in sub_schema.keys() + + +def is_enum(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "string" and "enum" in sub_schema.keys() + + +def is_integer(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "integer" + + +def is_array(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "array" + + +def is_boolean(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "boolean" + + +def is_number(sub_schema: SubSchema) -> bool: + return sub_schema["type"] == "number" + + +def resolve_reference(reference: str, schema: Schema) -> SubSchema: + elements = reference.split("/") + + if elements[0] != "#": + raise RuntimeError(f"Reference {reference} doesn't start with #") + + sub_schema = schema + for element in elements[1:]: + try: + sub_schema = sub_schema[element] + except KeyError as e: + raise RuntimeError(f"Failed to resolve reference {reference}") from e + + return sub_schema + + +def pascal_to_camel_case(input: str) -> str: + """Convert a pascalCase string to CamelCase. + All letters of following abbreviations are capitalized: + * AC + * DC + * EV + * EVSE + * V2X + * VPN + """ + if len(input) < 2: + return input.upper() + + if input[0:4] in ["evse", "ocsp"]: + return input[0:4].upper() + input[4:] + + # TODO: Handle cases like 'eventNotification", "eventData", "actualValue" etc. + if input[0:2] in ["ac", "dc", "ev"]: + return input[0:2].upper() + input[2:] + + if input[0:3] in ["v2x", "vpn"]: + return input[0:3].upper() + input[3:] + + return input[0:1].upper() + input[1:] diff --git a/scripts/v21/tests/__init__.py b/scripts/v21/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/v21/tests/fixtures/call_results/NotifyEVChargingNeedsRequest.json b/scripts/v21/tests/fixtures/call_results/NotifyEVChargingNeedsRequest.json new file mode 100644 index 000000000..7695e2e87 --- /dev/null +++ b/scripts/v21/tests/fixtures/call_results/NotifyEVChargingNeedsRequest.json @@ -0,0 +1,449 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:NotifyEVChargingNeedsRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ControlModeEnumType": { + "javaType": "ControlModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Scheduled", + "Dynamic" + ] + }, + "EnergyTransferModeEnumType": { + "javaType": "EnergyTransferModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "AC_single_phase", + "AC_two_phase", + "AC_three_phase", + "DC", + "AC_single_phase_BPT", + "AC_two_phase_BPT", + "AC_three_phase_BPT", + "DC_BPT", + "DC_ACDP", + "DC_ACDP_BPT", + "WPT" + ] + }, + "MobilityNeedsModeEnumType": { + "javaType": "MobilityNeedsModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "EVCC", + "EVCC_SECC" + ] + }, + "PricingEnumType": { + "javaType": "PricingEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "NoPricing", + "AbsolutePricing", + "PriceLevels" + ] + }, + "ACChargingParametersType": { + "javaType": "ACChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyAmount": { + "type": "number" + }, + "evMinCurrent": { + "type": "number" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + } + }, + "required": [ + "energyAmount", + "evMinCurrent", + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "ChargingNeedsType": { + "javaType": "ChargingNeeds", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "acChargingParameters": { + "$ref": "#/definitions/ACChargingParametersType" + }, + "requestedEnergyTransfer": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "dcChargingParameters": { + "$ref": "#/definitions/DCChargingParametersType" + }, + "v2xChargingParameters": { + "$ref": "#/definitions/V2XChargingParametersType" + }, + "availableEnergyTransfer": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "minItems": 1 + }, + "controlMode": { + "$ref": "#/definitions/ControlModeEnumType" + }, + "departureTime": { + "type": "string", + "format": "date-time" + }, + "evEnergyOffer": { + "$ref": "#/definitions/EVEnergyOfferType" + }, + "mobilityNeedsMode": { + "$ref": "#/definitions/MobilityNeedsModeEnumType" + }, + "pricing": { + "$ref": "#/definitions/PricingEnumType" + } + }, + "required": [ + "requestedEnergyTransfer" + ] + }, + "DCChargingParametersType": { + "javaType": "DCChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + }, + "evMaxPower": { + "type": "number" + }, + "evEnergyCapacity": { + "type": "number" + }, + "energyAmount": { + "type": "number" + }, + "stateOfCharge": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "fullSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "bulkSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + }, + "required": [ + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "EVAbsolutePriceScheduleEntryType": { + "javaType": "EVAbsolutePriceScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "evPriceRule": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPriceRuleType" + }, + "minItems": 1, + "maxItems": 8 + } + }, + "required": [ + "duration", + "evPriceRule" + ] + }, + "EVAbsolutePriceScheduleType": { + "javaType": "EVAbsolutePriceSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "evAbsolutePriceScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVAbsolutePriceScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "priceAlgorithm": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "timeAnchor", + "currency", + "priceAlgorithm", + "evAbsolutePriceScheduleEntries" + ] + }, + "EVEnergyOfferType": { + "javaType": "EVEnergyOffer", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evAbsolutePriceSchedule": { + "$ref": "#/definitions/EVAbsolutePriceScheduleType" + }, + "evPowerSchedule": { + "$ref": "#/definitions/EVPowerScheduleType" + } + }, + "required": [ + "evPowerSchedule" + ] + }, + "EVPowerScheduleEntryType": { + "javaType": "EVPowerScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "power": { + "type": "number" + } + }, + "required": [ + "duration", + "power" + ] + }, + "EVPowerScheduleType": { + "javaType": "EVPowerSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evPowerScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPowerScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "timeAnchor", + "evPowerScheduleEntries" + ] + }, + "EVPriceRuleType": { + "javaType": "EVPriceRule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyFee": { + "type": "number" + }, + "powerRangeStart": { + "type": "number" + } + }, + "required": [ + "energyFee", + "powerRangeStart" + ] + }, + "V2XChargingParametersType": { + "javaType": "V2XChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "minChargePower": { + "type": "number" + }, + "minChargePower_L2": { + "type": "number" + }, + "minChargePower_L3": { + "type": "number" + }, + "maxChargePower": { + "type": "number" + }, + "maxChargePower_L2": { + "type": "number" + }, + "maxChargePower_L3": { + "type": "number" + }, + "minDischargePower": { + "type": "number" + }, + "minDischargePower_L2": { + "type": "number" + }, + "minDischargePower_L3": { + "type": "number" + }, + "maxDischargePower": { + "type": "number" + }, + "maxDischargePower_L2": { + "type": "number" + }, + "maxDischargePower_L3": { + "type": "number" + }, + "minChargeCurrent": { + "type": "number" + }, + "maxChargeCurrent": { + "type": "number" + }, + "minDischargeCurrent": { + "type": "number" + }, + "maxDischargeCurrent": { + "type": "number" + }, + "minVoltage": { + "type": "number" + }, + "maxVoltage": { + "type": "number" + }, + "evTargetEnergyRequest": { + "type": "number" + }, + "evMinEnergyRequest": { + "type": "number" + }, + "evMaxEnergyRequest": { + "type": "number" + }, + "evMinV2XEnergyRequest": { + "type": "number" + }, + "evMaxV2XEnergyRequest": { + "type": "number" + }, + "targetSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + } + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evseId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "maxScheduleTuples": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "chargingNeeds": { + "$ref": "#/definitions/ChargingNeedsType" + } + }, + "required": [ + "evseId", + "chargingNeeds" + ] +} diff --git a/scripts/v21/tests/fixtures/calls/DataTransferRequest.json b/scripts/v21/tests/fixtures/calls/DataTransferRequest.json new file mode 100644 index 000000000..c936e4d38 --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/DataTransferRequest.json @@ -0,0 +1,40 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:DataTransferRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "messageId": { + "type": "string", + "maxLength": 50 + }, + "data": {}, + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/calls/Get15118EVCertificateRequest.json b/scripts/v21/tests/fixtures/calls/Get15118EVCertificateRequest.json new file mode 100644 index 000000000..7e5e0122f --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/Get15118EVCertificateRequest.json @@ -0,0 +1,68 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:Get15118EVCertificateRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "CertificateActionEnumType": { + "javaType": "CertificateActionEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Install", + "Update" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "iso15118SchemaVersion": { + "type": "string", + "maxLength": 50 + }, + "action": { + "$ref": "#/definitions/CertificateActionEnumType" + }, + "exiRequest": { + "type": "string", + "maxLength": 11000 + }, + "maximumContractCertificateChains": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "prioritizedEMAIDs": { + "type": "array", + "additionalItems": false, + "items": { + "type": "string", + "maxLength": 255 + }, + "minItems": 1, + "maxItems": 8 + } + }, + "required": [ + "iso15118SchemaVersion", + "action", + "exiRequest" + ] +} diff --git a/scripts/v21/tests/fixtures/calls/GetCertificateStatusRequest.json b/scripts/v21/tests/fixtures/calls/GetCertificateStatusRequest.json new file mode 100644 index 000000000..d702dd62d --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/GetCertificateStatusRequest.json @@ -0,0 +1,80 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:GetCertificateStatusRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "HashAlgorithmEnumType": { + "javaType": "HashAlgorithmEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "SHA256", + "SHA384", + "SHA512" + ] + }, + "OCSPRequestDataType": { + "javaType": "OCSPRequestData", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "hashAlgorithm": { + "$ref": "#/definitions/HashAlgorithmEnumType" + }, + "issuerNameHash": { + "type": "string", + "maxLength": 128 + }, + "issuerKeyHash": { + "type": "string", + "maxLength": 128 + }, + "serialNumber": { + "type": "string", + "maxLength": 40 + }, + "responderURL": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "hashAlgorithm", + "issuerNameHash", + "issuerKeyHash", + "serialNumber", + "responderURL" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "ocspRequestData": { + "$ref": "#/definitions/OCSPRequestDataType" + } + }, + "required": [ + "ocspRequestData" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/calls/GetInstalledCertificateIdsRequest.json b/scripts/v21/tests/fixtures/calls/GetInstalledCertificateIdsRequest.json new file mode 100644 index 000000000..df4c49b0d --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/GetInstalledCertificateIdsRequest.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:GetInstalledCertificateIdsRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "GetCertificateIdUseEnumType": { + "javaType": "GetCertificateIdUseEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "V2GRootCertificate", + "MORootCertificate", + "CSMSRootCertificate", + "V2GCertificateChain", + "ManufacturerRootCertificate", + "OEMRootCertificate" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "certificateType": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/GetCertificateIdUseEnumType" + }, + "minItems": 1 + } + } +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/calls/NotifyEVChargingNeedsRequest.json b/scripts/v21/tests/fixtures/calls/NotifyEVChargingNeedsRequest.json new file mode 100644 index 000000000..7695e2e87 --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/NotifyEVChargingNeedsRequest.json @@ -0,0 +1,449 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:NotifyEVChargingNeedsRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ControlModeEnumType": { + "javaType": "ControlModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Scheduled", + "Dynamic" + ] + }, + "EnergyTransferModeEnumType": { + "javaType": "EnergyTransferModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "AC_single_phase", + "AC_two_phase", + "AC_three_phase", + "DC", + "AC_single_phase_BPT", + "AC_two_phase_BPT", + "AC_three_phase_BPT", + "DC_BPT", + "DC_ACDP", + "DC_ACDP_BPT", + "WPT" + ] + }, + "MobilityNeedsModeEnumType": { + "javaType": "MobilityNeedsModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "EVCC", + "EVCC_SECC" + ] + }, + "PricingEnumType": { + "javaType": "PricingEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "NoPricing", + "AbsolutePricing", + "PriceLevels" + ] + }, + "ACChargingParametersType": { + "javaType": "ACChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyAmount": { + "type": "number" + }, + "evMinCurrent": { + "type": "number" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + } + }, + "required": [ + "energyAmount", + "evMinCurrent", + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "ChargingNeedsType": { + "javaType": "ChargingNeeds", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "acChargingParameters": { + "$ref": "#/definitions/ACChargingParametersType" + }, + "requestedEnergyTransfer": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "dcChargingParameters": { + "$ref": "#/definitions/DCChargingParametersType" + }, + "v2xChargingParameters": { + "$ref": "#/definitions/V2XChargingParametersType" + }, + "availableEnergyTransfer": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "minItems": 1 + }, + "controlMode": { + "$ref": "#/definitions/ControlModeEnumType" + }, + "departureTime": { + "type": "string", + "format": "date-time" + }, + "evEnergyOffer": { + "$ref": "#/definitions/EVEnergyOfferType" + }, + "mobilityNeedsMode": { + "$ref": "#/definitions/MobilityNeedsModeEnumType" + }, + "pricing": { + "$ref": "#/definitions/PricingEnumType" + } + }, + "required": [ + "requestedEnergyTransfer" + ] + }, + "DCChargingParametersType": { + "javaType": "DCChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + }, + "evMaxPower": { + "type": "number" + }, + "evEnergyCapacity": { + "type": "number" + }, + "energyAmount": { + "type": "number" + }, + "stateOfCharge": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "fullSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "bulkSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + }, + "required": [ + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "EVAbsolutePriceScheduleEntryType": { + "javaType": "EVAbsolutePriceScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "evPriceRule": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPriceRuleType" + }, + "minItems": 1, + "maxItems": 8 + } + }, + "required": [ + "duration", + "evPriceRule" + ] + }, + "EVAbsolutePriceScheduleType": { + "javaType": "EVAbsolutePriceSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "evAbsolutePriceScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVAbsolutePriceScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "priceAlgorithm": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "timeAnchor", + "currency", + "priceAlgorithm", + "evAbsolutePriceScheduleEntries" + ] + }, + "EVEnergyOfferType": { + "javaType": "EVEnergyOffer", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evAbsolutePriceSchedule": { + "$ref": "#/definitions/EVAbsolutePriceScheduleType" + }, + "evPowerSchedule": { + "$ref": "#/definitions/EVPowerScheduleType" + } + }, + "required": [ + "evPowerSchedule" + ] + }, + "EVPowerScheduleEntryType": { + "javaType": "EVPowerScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "power": { + "type": "number" + } + }, + "required": [ + "duration", + "power" + ] + }, + "EVPowerScheduleType": { + "javaType": "EVPowerSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evPowerScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPowerScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "timeAnchor", + "evPowerScheduleEntries" + ] + }, + "EVPriceRuleType": { + "javaType": "EVPriceRule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyFee": { + "type": "number" + }, + "powerRangeStart": { + "type": "number" + } + }, + "required": [ + "energyFee", + "powerRangeStart" + ] + }, + "V2XChargingParametersType": { + "javaType": "V2XChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "minChargePower": { + "type": "number" + }, + "minChargePower_L2": { + "type": "number" + }, + "minChargePower_L3": { + "type": "number" + }, + "maxChargePower": { + "type": "number" + }, + "maxChargePower_L2": { + "type": "number" + }, + "maxChargePower_L3": { + "type": "number" + }, + "minDischargePower": { + "type": "number" + }, + "minDischargePower_L2": { + "type": "number" + }, + "minDischargePower_L3": { + "type": "number" + }, + "maxDischargePower": { + "type": "number" + }, + "maxDischargePower_L2": { + "type": "number" + }, + "maxDischargePower_L3": { + "type": "number" + }, + "minChargeCurrent": { + "type": "number" + }, + "maxChargeCurrent": { + "type": "number" + }, + "minDischargeCurrent": { + "type": "number" + }, + "maxDischargeCurrent": { + "type": "number" + }, + "minVoltage": { + "type": "number" + }, + "maxVoltage": { + "type": "number" + }, + "evTargetEnergyRequest": { + "type": "number" + }, + "evMinEnergyRequest": { + "type": "number" + }, + "evMaxEnergyRequest": { + "type": "number" + }, + "evMinV2XEnergyRequest": { + "type": "number" + }, + "evMaxV2XEnergyRequest": { + "type": "number" + }, + "targetSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + } + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evseId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "maxScheduleTuples": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "chargingNeeds": { + "$ref": "#/definitions/ChargingNeedsType" + } + }, + "required": [ + "evseId", + "chargingNeeds" + ] +} diff --git a/scripts/v21/tests/fixtures/calls/NotifyReportRequest.json b/scripts/v21/tests/fixtures/calls/NotifyReportRequest.json new file mode 100644 index 000000000..5296e7715 --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/NotifyReportRequest.json @@ -0,0 +1,260 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:NotifyReportRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "AttributeEnumType": { + "javaType": "AttributeEnum", + "type": "string", + "default": "Actual", + "additionalProperties": false, + "enum": [ + "Actual", + "Target", + "MinSet", + "MaxSet" + ] + }, + "DataEnumType": { + "javaType": "DataEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "string", + "decimal", + "integer", + "dateTime", + "boolean", + "OptionList", + "SequenceList", + "MemberList" + ] + }, + "MutabilityEnumType": { + "javaType": "MutabilityEnum", + "type": "string", + "default": "ReadWrite", + "additionalProperties": false, + "enum": [ + "ReadOnly", + "WriteOnly", + "ReadWrite" + ] + }, + "ComponentType": { + "javaType": "Component", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evse": { + "$ref": "#/definitions/EVSEType" + }, + "name": { + "type": "string", + "maxLength": 50 + }, + "instance": { + "type": "string", + "maxLength": 50 + } + }, + "required": [ + "name" + ] + }, + "EVSEType": { + "javaType": "EVSE", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "id": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "connectorId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + } + }, + "required": [ + "id" + ] + }, + "ReportDataType": { + "javaType": "ReportData", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "component": { + "$ref": "#/definitions/ComponentType" + }, + "variable": { + "$ref": "#/definitions/VariableType" + }, + "variableAttribute": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/VariableAttributeType" + }, + "minItems": 1, + "maxItems": 4 + }, + "variableCharacteristics": { + "$ref": "#/definitions/VariableCharacteristicsType" + } + }, + "required": [ + "component", + "variable", + "variableAttribute" + ] + }, + "VariableAttributeType": { + "javaType": "VariableAttribute", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "type": { + "$ref": "#/definitions/AttributeEnumType" + }, + "value": { + "type": "string", + "maxLength": 2500 + }, + "mutability": { + "$ref": "#/definitions/MutabilityEnumType" + }, + "persistent": { + "type": "boolean", + "default": false + }, + "constant": { + "type": "boolean", + "default": false + } + } + }, + "VariableCharacteristicsType": { + "javaType": "VariableCharacteristics", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "unit": { + "type": "string", + "maxLength": 16 + }, + "dataType": { + "$ref": "#/definitions/DataEnumType" + }, + "minLimit": { + "type": "number" + }, + "maxLimit": { + "type": "number" + }, + "valuesList": { + "type": "string", + "maxLength": 1000 + }, + "supportsMonitoring": { + "type": "boolean" + } + }, + "required": [ + "dataType", + "supportsMonitoring" + ] + }, + "VariableType": { + "javaType": "Variable", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "name": { + "type": "string", + "maxLength": 50 + }, + "instance": { + "type": "string", + "maxLength": 50 + } + }, + "required": [ + "name" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "requestId": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "generatedAt": { + "type": "string", + "format": "date-time" + }, + "reportData": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/ReportDataType" + }, + "minItems": 1 + }, + "tbc": { + "type": "boolean", + "default": false + }, + "seqNo": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + } + }, + "required": [ + "requestId", + "generatedAt", + "seqNo" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/calls/ResetRequest.json b/scripts/v21/tests/fixtures/calls/ResetRequest.json new file mode 100644 index 000000000..3e7018c8a --- /dev/null +++ b/scripts/v21/tests/fixtures/calls/ResetRequest.json @@ -0,0 +1,48 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:ResetRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ResetEnumType": { + "javaType": "ResetEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Immediate", + "OnIdle" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "type": { + "$ref": "#/definitions/ResetEnumType" + }, + "evseId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + } + }, + "required": [ + "type" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/datatypes/ChangeAvailabilityRequest.json b/scripts/v21/tests/fixtures/datatypes/ChangeAvailabilityRequest.json new file mode 100644 index 000000000..92cb05d67 --- /dev/null +++ b/scripts/v21/tests/fixtures/datatypes/ChangeAvailabilityRequest.json @@ -0,0 +1,69 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:ChangeAvailabilityRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "OperationalStatusEnumType": { + "javaType": "OperationalStatusEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Inoperative", + "Operative" + ] + }, + "EVSEType": { + "javaType": "EVSE", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "id": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "connectorId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + } + }, + "required": [ + "id" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evse": { + "$ref": "#/definitions/EVSEType" + }, + "operationalStatus": { + "$ref": "#/definitions/OperationalStatusEnumType" + } + }, + "required": [ + "operationalStatus" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/datatypes/GetCertificateStatusRequest.json b/scripts/v21/tests/fixtures/datatypes/GetCertificateStatusRequest.json new file mode 100644 index 000000000..d702dd62d --- /dev/null +++ b/scripts/v21/tests/fixtures/datatypes/GetCertificateStatusRequest.json @@ -0,0 +1,80 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:GetCertificateStatusRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "HashAlgorithmEnumType": { + "javaType": "HashAlgorithmEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "SHA256", + "SHA384", + "SHA512" + ] + }, + "OCSPRequestDataType": { + "javaType": "OCSPRequestData", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "hashAlgorithm": { + "$ref": "#/definitions/HashAlgorithmEnumType" + }, + "issuerNameHash": { + "type": "string", + "maxLength": 128 + }, + "issuerKeyHash": { + "type": "string", + "maxLength": 128 + }, + "serialNumber": { + "type": "string", + "maxLength": 40 + }, + "responderURL": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "hashAlgorithm", + "issuerNameHash", + "issuerKeyHash", + "serialNumber", + "responderURL" + ] + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "ocspRequestData": { + "$ref": "#/definitions/OCSPRequestDataType" + } + }, + "required": [ + "ocspRequestData" + ] +} \ No newline at end of file diff --git a/scripts/v21/tests/fixtures/datatypes/NotifyEVChargingNeedsRequest.json b/scripts/v21/tests/fixtures/datatypes/NotifyEVChargingNeedsRequest.json new file mode 100644 index 000000000..7695e2e87 --- /dev/null +++ b/scripts/v21/tests/fixtures/datatypes/NotifyEVChargingNeedsRequest.json @@ -0,0 +1,449 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:NotifyEVChargingNeedsRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ControlModeEnumType": { + "javaType": "ControlModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Scheduled", + "Dynamic" + ] + }, + "EnergyTransferModeEnumType": { + "javaType": "EnergyTransferModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "AC_single_phase", + "AC_two_phase", + "AC_three_phase", + "DC", + "AC_single_phase_BPT", + "AC_two_phase_BPT", + "AC_three_phase_BPT", + "DC_BPT", + "DC_ACDP", + "DC_ACDP_BPT", + "WPT" + ] + }, + "MobilityNeedsModeEnumType": { + "javaType": "MobilityNeedsModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "EVCC", + "EVCC_SECC" + ] + }, + "PricingEnumType": { + "javaType": "PricingEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "NoPricing", + "AbsolutePricing", + "PriceLevels" + ] + }, + "ACChargingParametersType": { + "javaType": "ACChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyAmount": { + "type": "number" + }, + "evMinCurrent": { + "type": "number" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + } + }, + "required": [ + "energyAmount", + "evMinCurrent", + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "ChargingNeedsType": { + "javaType": "ChargingNeeds", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "acChargingParameters": { + "$ref": "#/definitions/ACChargingParametersType" + }, + "requestedEnergyTransfer": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "dcChargingParameters": { + "$ref": "#/definitions/DCChargingParametersType" + }, + "v2xChargingParameters": { + "$ref": "#/definitions/V2XChargingParametersType" + }, + "availableEnergyTransfer": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "minItems": 1 + }, + "controlMode": { + "$ref": "#/definitions/ControlModeEnumType" + }, + "departureTime": { + "type": "string", + "format": "date-time" + }, + "evEnergyOffer": { + "$ref": "#/definitions/EVEnergyOfferType" + }, + "mobilityNeedsMode": { + "$ref": "#/definitions/MobilityNeedsModeEnumType" + }, + "pricing": { + "$ref": "#/definitions/PricingEnumType" + } + }, + "required": [ + "requestedEnergyTransfer" + ] + }, + "DCChargingParametersType": { + "javaType": "DCChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + }, + "evMaxPower": { + "type": "number" + }, + "evEnergyCapacity": { + "type": "number" + }, + "energyAmount": { + "type": "number" + }, + "stateOfCharge": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "fullSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "bulkSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + }, + "required": [ + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "EVAbsolutePriceScheduleEntryType": { + "javaType": "EVAbsolutePriceScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "evPriceRule": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPriceRuleType" + }, + "minItems": 1, + "maxItems": 8 + } + }, + "required": [ + "duration", + "evPriceRule" + ] + }, + "EVAbsolutePriceScheduleType": { + "javaType": "EVAbsolutePriceSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "evAbsolutePriceScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVAbsolutePriceScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "priceAlgorithm": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "timeAnchor", + "currency", + "priceAlgorithm", + "evAbsolutePriceScheduleEntries" + ] + }, + "EVEnergyOfferType": { + "javaType": "EVEnergyOffer", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evAbsolutePriceSchedule": { + "$ref": "#/definitions/EVAbsolutePriceScheduleType" + }, + "evPowerSchedule": { + "$ref": "#/definitions/EVPowerScheduleType" + } + }, + "required": [ + "evPowerSchedule" + ] + }, + "EVPowerScheduleEntryType": { + "javaType": "EVPowerScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "power": { + "type": "number" + } + }, + "required": [ + "duration", + "power" + ] + }, + "EVPowerScheduleType": { + "javaType": "EVPowerSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evPowerScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPowerScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "timeAnchor", + "evPowerScheduleEntries" + ] + }, + "EVPriceRuleType": { + "javaType": "EVPriceRule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyFee": { + "type": "number" + }, + "powerRangeStart": { + "type": "number" + } + }, + "required": [ + "energyFee", + "powerRangeStart" + ] + }, + "V2XChargingParametersType": { + "javaType": "V2XChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "minChargePower": { + "type": "number" + }, + "minChargePower_L2": { + "type": "number" + }, + "minChargePower_L3": { + "type": "number" + }, + "maxChargePower": { + "type": "number" + }, + "maxChargePower_L2": { + "type": "number" + }, + "maxChargePower_L3": { + "type": "number" + }, + "minDischargePower": { + "type": "number" + }, + "minDischargePower_L2": { + "type": "number" + }, + "minDischargePower_L3": { + "type": "number" + }, + "maxDischargePower": { + "type": "number" + }, + "maxDischargePower_L2": { + "type": "number" + }, + "maxDischargePower_L3": { + "type": "number" + }, + "minChargeCurrent": { + "type": "number" + }, + "maxChargeCurrent": { + "type": "number" + }, + "minDischargeCurrent": { + "type": "number" + }, + "maxDischargeCurrent": { + "type": "number" + }, + "minVoltage": { + "type": "number" + }, + "maxVoltage": { + "type": "number" + }, + "evTargetEnergyRequest": { + "type": "number" + }, + "evMinEnergyRequest": { + "type": "number" + }, + "evMaxEnergyRequest": { + "type": "number" + }, + "evMinV2XEnergyRequest": { + "type": "number" + }, + "evMaxV2XEnergyRequest": { + "type": "number" + }, + "targetSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + } + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evseId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "maxScheduleTuples": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "chargingNeeds": { + "$ref": "#/definitions/ChargingNeedsType" + } + }, + "required": [ + "evseId", + "chargingNeeds" + ] +} diff --git a/scripts/v21/tests/fixtures/enums/NotifyEVChargingNeedsRequest.json b/scripts/v21/tests/fixtures/enums/NotifyEVChargingNeedsRequest.json new file mode 100644 index 000000000..7695e2e87 --- /dev/null +++ b/scripts/v21/tests/fixtures/enums/NotifyEVChargingNeedsRequest.json @@ -0,0 +1,449 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$id": "urn:OCPP:Cp:2:2023:5:NotifyEVChargingNeedsRequest", + "comment": "OCPP 2.1 Draft 1, Copyright Open Charge Alliance", + "definitions": { + "CustomDataType": { + "description": "This class does not get 'AdditionalProperties = false' in the schema generation, so it can be extended with arbitrary JSON properties to allow adding custom data.", + "javaType": "CustomData", + "type": "object", + "properties": { + "vendorId": { + "type": "string", + "maxLength": 255 + } + }, + "required": [ + "vendorId" + ] + }, + "ControlModeEnumType": { + "javaType": "ControlModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "Scheduled", + "Dynamic" + ] + }, + "EnergyTransferModeEnumType": { + "javaType": "EnergyTransferModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "AC_single_phase", + "AC_two_phase", + "AC_three_phase", + "DC", + "AC_single_phase_BPT", + "AC_two_phase_BPT", + "AC_three_phase_BPT", + "DC_BPT", + "DC_ACDP", + "DC_ACDP_BPT", + "WPT" + ] + }, + "MobilityNeedsModeEnumType": { + "javaType": "MobilityNeedsModeEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "EVCC", + "EVCC_SECC" + ] + }, + "PricingEnumType": { + "javaType": "PricingEnum", + "type": "string", + "additionalProperties": false, + "enum": [ + "NoPricing", + "AbsolutePricing", + "PriceLevels" + ] + }, + "ACChargingParametersType": { + "javaType": "ACChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyAmount": { + "type": "number" + }, + "evMinCurrent": { + "type": "number" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + } + }, + "required": [ + "energyAmount", + "evMinCurrent", + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "ChargingNeedsType": { + "javaType": "ChargingNeeds", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "acChargingParameters": { + "$ref": "#/definitions/ACChargingParametersType" + }, + "requestedEnergyTransfer": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "dcChargingParameters": { + "$ref": "#/definitions/DCChargingParametersType" + }, + "v2xChargingParameters": { + "$ref": "#/definitions/V2XChargingParametersType" + }, + "availableEnergyTransfer": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EnergyTransferModeEnumType" + }, + "minItems": 1 + }, + "controlMode": { + "$ref": "#/definitions/ControlModeEnumType" + }, + "departureTime": { + "type": "string", + "format": "date-time" + }, + "evEnergyOffer": { + "$ref": "#/definitions/EVEnergyOfferType" + }, + "mobilityNeedsMode": { + "$ref": "#/definitions/MobilityNeedsModeEnumType" + }, + "pricing": { + "$ref": "#/definitions/PricingEnumType" + } + }, + "required": [ + "requestedEnergyTransfer" + ] + }, + "DCChargingParametersType": { + "javaType": "DCChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evMaxCurrent": { + "type": "number" + }, + "evMaxVoltage": { + "type": "number" + }, + "evMaxPower": { + "type": "number" + }, + "evEnergyCapacity": { + "type": "number" + }, + "energyAmount": { + "type": "number" + }, + "stateOfCharge": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "fullSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + }, + "bulkSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + }, + "required": [ + "evMaxCurrent", + "evMaxVoltage" + ] + }, + "EVAbsolutePriceScheduleEntryType": { + "javaType": "EVAbsolutePriceScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "evPriceRule": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPriceRuleType" + }, + "minItems": 1, + "maxItems": 8 + } + }, + "required": [ + "duration", + "evPriceRule" + ] + }, + "EVAbsolutePriceScheduleType": { + "javaType": "EVAbsolutePriceSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + }, + "currency": { + "type": "string", + "maxLength": 3 + }, + "evAbsolutePriceScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVAbsolutePriceScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "priceAlgorithm": { + "type": "string", + "maxLength": 512 + } + }, + "required": [ + "timeAnchor", + "currency", + "priceAlgorithm", + "evAbsolutePriceScheduleEntries" + ] + }, + "EVEnergyOfferType": { + "javaType": "EVEnergyOffer", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evAbsolutePriceSchedule": { + "$ref": "#/definitions/EVAbsolutePriceScheduleType" + }, + "evPowerSchedule": { + "$ref": "#/definitions/EVPowerScheduleType" + } + }, + "required": [ + "evPowerSchedule" + ] + }, + "EVPowerScheduleEntryType": { + "javaType": "EVPowerScheduleEntry", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "duration": { + "type": "integer", + "minimum": -2147483648.0, + "maximum": 2147483647.0 + }, + "power": { + "type": "number" + } + }, + "required": [ + "duration", + "power" + ] + }, + "EVPowerScheduleType": { + "javaType": "EVPowerSchedule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evPowerScheduleEntries": { + "type": "array", + "additionalItems": false, + "items": { + "$ref": "#/definitions/EVPowerScheduleEntryType" + }, + "minItems": 1, + "maxItems": 1024 + }, + "timeAnchor": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "timeAnchor", + "evPowerScheduleEntries" + ] + }, + "EVPriceRuleType": { + "javaType": "EVPriceRule", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "energyFee": { + "type": "number" + }, + "powerRangeStart": { + "type": "number" + } + }, + "required": [ + "energyFee", + "powerRangeStart" + ] + }, + "V2XChargingParametersType": { + "javaType": "V2XChargingParameters", + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "minChargePower": { + "type": "number" + }, + "minChargePower_L2": { + "type": "number" + }, + "minChargePower_L3": { + "type": "number" + }, + "maxChargePower": { + "type": "number" + }, + "maxChargePower_L2": { + "type": "number" + }, + "maxChargePower_L3": { + "type": "number" + }, + "minDischargePower": { + "type": "number" + }, + "minDischargePower_L2": { + "type": "number" + }, + "minDischargePower_L3": { + "type": "number" + }, + "maxDischargePower": { + "type": "number" + }, + "maxDischargePower_L2": { + "type": "number" + }, + "maxDischargePower_L3": { + "type": "number" + }, + "minChargeCurrent": { + "type": "number" + }, + "maxChargeCurrent": { + "type": "number" + }, + "minDischargeCurrent": { + "type": "number" + }, + "maxDischargeCurrent": { + "type": "number" + }, + "minVoltage": { + "type": "number" + }, + "maxVoltage": { + "type": "number" + }, + "evTargetEnergyRequest": { + "type": "number" + }, + "evMinEnergyRequest": { + "type": "number" + }, + "evMaxEnergyRequest": { + "type": "number" + }, + "evMinV2XEnergyRequest": { + "type": "number" + }, + "evMaxV2XEnergyRequest": { + "type": "number" + }, + "targetSoC": { + "type": "integer", + "minimum": 0.0, + "maximum": 100.0 + } + } + } + }, + "type": "object", + "additionalProperties": false, + "properties": { + "customData": { + "$ref": "#/definitions/CustomDataType" + }, + "evseId": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "maxScheduleTuples": { + "type": "integer", + "minimum": 0.0, + "maximum": 2147483647.0 + }, + "chargingNeeds": { + "$ref": "#/definitions/ChargingNeedsType" + } + }, + "required": [ + "evseId", + "chargingNeeds" + ] +} diff --git a/scripts/v21/tests/golden_files/call_results/NotifyEVChargingNeedsRequest.py b/scripts/v21/tests/golden_files/call_results/NotifyEVChargingNeedsRequest.py new file mode 100644 index 000000000..5b5688730 --- /dev/null +++ b/scripts/v21/tests/golden_files/call_results/NotifyEVChargingNeedsRequest.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + ChargingNeeds, + CustomData, +) + + +@dataclass +class NotifyEVChargingNeeds: + charging_needs: ChargingNeeds + evse_id: int + custom_data: Optional[CustomData] = None + max_schedule_tuples: Optional[int] = None diff --git a/scripts/v21/tests/golden_files/calls/DataTransferRequest.py b/scripts/v21/tests/golden_files/calls/DataTransferRequest.py new file mode 100644 index 000000000..b726931d0 --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/DataTransferRequest.py @@ -0,0 +1,14 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, +) + + +@dataclass +class DataTransfer: + vendor_id: str + custom_data: Optional[CustomData] = None + data: Any = None + message_id: Optional[str] = None diff --git a/scripts/v21/tests/golden_files/calls/Get15118EVCertificateRequest.py b/scripts/v21/tests/golden_files/calls/Get15118EVCertificateRequest.py new file mode 100644 index 000000000..15d573e7c --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/Get15118EVCertificateRequest.py @@ -0,0 +1,20 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, +) + +from enums import ( + CertificateAction, +) + + +@dataclass +class Get15118EVCertificate: + action: CertificateAction + exi_request: str + iso_15118_schema_version: str + custom_data: Optional[CustomData] = None + maximum_contract_certificate_chains: Optional[int] = None + prioritized_emaids: Optional[List[str]] = None diff --git a/scripts/v21/tests/golden_files/calls/GetCertificateStatusRequest.py b/scripts/v21/tests/golden_files/calls/GetCertificateStatusRequest.py new file mode 100644 index 000000000..f2fcdda23 --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/GetCertificateStatusRequest.py @@ -0,0 +1,13 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, + OCSPRequestData, +) + + +@dataclass +class GetCertificateStatus: + ocsp_request_data: OCSPRequestData + custom_data: Optional[CustomData] = None diff --git a/scripts/v21/tests/golden_files/calls/GetInstalledCertificateIdsRequest.py b/scripts/v21/tests/golden_files/calls/GetInstalledCertificateIdsRequest.py new file mode 100644 index 000000000..1149fe81b --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/GetInstalledCertificateIdsRequest.py @@ -0,0 +1,16 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, +) + +from enums import ( + GetCertificateIdUse, +) + + +@dataclass +class GetInstalledCertificateIds: + certificate_type: Optional[List[GetCertificateIdUse]] = None + custom_data: Optional[CustomData] = None diff --git a/scripts/v21/tests/golden_files/calls/NotifyEVChargingNeedsRequest.py b/scripts/v21/tests/golden_files/calls/NotifyEVChargingNeedsRequest.py new file mode 100644 index 000000000..5b5688730 --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/NotifyEVChargingNeedsRequest.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + ChargingNeeds, + CustomData, +) + + +@dataclass +class NotifyEVChargingNeeds: + charging_needs: ChargingNeeds + evse_id: int + custom_data: Optional[CustomData] = None + max_schedule_tuples: Optional[int] = None diff --git a/scripts/v21/tests/golden_files/calls/NotifyReportRequest.py b/scripts/v21/tests/golden_files/calls/NotifyReportRequest.py new file mode 100644 index 000000000..598884aad --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/NotifyReportRequest.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, + ReportData, +) + + +@dataclass +class NotifyReport: + generated_at: str + request_id: int + seq_no: int + custom_data: Optional[CustomData] = None + report_data: Optional[List[ReportData]] = None + tbc: bool = False diff --git a/scripts/v21/tests/golden_files/calls/ResetRequest.py b/scripts/v21/tests/golden_files/calls/ResetRequest.py new file mode 100644 index 000000000..3302f7282 --- /dev/null +++ b/scripts/v21/tests/golden_files/calls/ResetRequest.py @@ -0,0 +1,15 @@ +from dataclasses import dataclass +from typing import Any, List, Optional + +from datatypes import ( + CustomData, +) + +import enums + + +@dataclass +class Reset: + type: enums.Reset + custom_data: Optional[CustomData] = None + evse_id: Optional[int] = None diff --git a/scripts/v21/tests/golden_files/datatypes/ChangeAvailabilityRequest.py b/scripts/v21/tests/golden_files/datatypes/ChangeAvailabilityRequest.py new file mode 100644 index 000000000..65c7f1757 --- /dev/null +++ b/scripts/v21/tests/golden_files/datatypes/ChangeAvailabilityRequest.py @@ -0,0 +1,15 @@ +from __future__ import annotations +from typing import List, Optional +from dataclasses import dataclass + + +@dataclass +class CustomData: + vendor_id: str + + +@dataclass +class EVSE: + id: int + connector_id: Optional[int] = None + custom_data: Optional[CustomData] = None diff --git a/scripts/v21/tests/golden_files/datatypes/GetCertificateStatusRequest.py b/scripts/v21/tests/golden_files/datatypes/GetCertificateStatusRequest.py new file mode 100644 index 000000000..f0500d420 --- /dev/null +++ b/scripts/v21/tests/golden_files/datatypes/GetCertificateStatusRequest.py @@ -0,0 +1,22 @@ +from __future__ import annotations +from typing import List, Optional +from dataclasses import dataclass + +from enums import ( + HashAlgorithm, +) + + +@dataclass +class CustomData: + vendor_id: str + + +@dataclass +class OCSPRequestData: + hash_algorithm: HashAlgorithm + issuer_key_hash: str + issuer_name_hash: str + responder_url: str + serial_number: str + custom_data: Optional[CustomData] = None diff --git a/scripts/v21/tests/golden_files/datatypes/NotifyEVChargingNeedsRequest.py b/scripts/v21/tests/golden_files/datatypes/NotifyEVChargingNeedsRequest.py new file mode 100644 index 000000000..4910aae9a --- /dev/null +++ b/scripts/v21/tests/golden_files/datatypes/NotifyEVChargingNeedsRequest.py @@ -0,0 +1,125 @@ +from __future__ import annotations +from typing import List, Optional +from dataclasses import dataclass + +from enums import ( + ControlMode, + EnergyTransferMode, + MobilityNeedsMode, + Pricing, +) + + +@dataclass +class ACChargingParameters: + energy_amount: float + ev_max_current: float + ev_max_voltage: float + ev_min_current: float + custom_data: Optional[CustomData] = None + + +@dataclass +class ChargingNeeds: + requested_energy_transfer: EnergyTransferMode + ac_charging_parameters: Optional[ACChargingParameters] = None + available_energy_transfer: Optional[List[EnergyTransferMode]] = None + control_mode: Optional[ControlMode] = None + custom_data: Optional[CustomData] = None + dc_charging_parameters: Optional[DCChargingParameters] = None + departure_time: Optional[str] = None + ev_energy_offer: Optional[EVEnergyOffer] = None + mobility_needs_mode: Optional[MobilityNeedsMode] = None + pricing: Optional[Pricing] = None + v2x_charging_parameters: Optional[V2XChargingParameters] = None + + +@dataclass +class CustomData: + vendor_id: str + + +@dataclass +class DCChargingParameters: + ev_max_current: float + ev_max_voltage: float + bulk_soc: Optional[int] = None + custom_data: Optional[CustomData] = None + energy_amount: Optional[float] = None + ev_energy_capacity: Optional[float] = None + ev_max_power: Optional[float] = None + full_soc: Optional[int] = None + state_of_charge: Optional[int] = None + + +@dataclass +class EVAbsolutePriceSchedule: + currency: str + ev_absolute_price_schedule_entries: List[EVAbsolutePriceScheduleEntry] + price_algorithm: str + time_anchor: str + custom_data: Optional[CustomData] = None + + +@dataclass +class EVAbsolutePriceScheduleEntry: + duration: int + ev_price_rule: List[EVPriceRule] + custom_data: Optional[CustomData] = None + + +@dataclass +class EVEnergyOffer: + ev_power_schedule: EVPowerSchedule + custom_data: Optional[CustomData] = None + ev_absolute_price_schedule: Optional[EVAbsolutePriceSchedule] = None + + +@dataclass +class EVPowerSchedule: + ev_power_schedule_entries: List[EVPowerScheduleEntry] + time_anchor: str + custom_data: Optional[CustomData] = None + + +@dataclass +class EVPowerScheduleEntry: + duration: int + power: float + custom_data: Optional[CustomData] = None + + +@dataclass +class EVPriceRule: + energy_fee: float + power_range_start: float + custom_data: Optional[CustomData] = None + + +@dataclass +class V2XChargingParameters: + custom_data: Optional[CustomData] = None + ev_max_energy_request: Optional[float] = None + ev_max_v2x_energy_request: Optional[float] = None + ev_min_energy_request: Optional[float] = None + ev_min_v2x_energy_request: Optional[float] = None + ev_target_energy_request: Optional[float] = None + max_charge_current: Optional[float] = None + max_charge_power: Optional[float] = None + max_charge_power_l2: Optional[float] = None + max_charge_power_l3: Optional[float] = None + max_discharge_current: Optional[float] = None + max_discharge_power: Optional[float] = None + max_discharge_power_l2: Optional[float] = None + max_discharge_power_l3: Optional[float] = None + max_voltage: Optional[float] = None + min_charge_current: Optional[float] = None + min_charge_power: Optional[float] = None + min_charge_power_l2: Optional[float] = None + min_charge_power_l3: Optional[float] = None + min_discharge_current: Optional[float] = None + min_discharge_power: Optional[float] = None + min_discharge_power_l2: Optional[float] = None + min_discharge_power_l3: Optional[float] = None + min_voltage: Optional[float] = None + target_soc: Optional[int] = None diff --git a/scripts/v21/tests/golden_files/enums/NotifyEVChargingNeedsRequest.py b/scripts/v21/tests/golden_files/enums/NotifyEVChargingNeedsRequest.py new file mode 100644 index 000000000..2acde7c13 --- /dev/null +++ b/scripts/v21/tests/golden_files/enums/NotifyEVChargingNeedsRequest.py @@ -0,0 +1,38 @@ +try: + # breaking change introduced in python 3.11 + from enum import StrEnum +except ImportError: # pragma: no cover + from enum import Enum # pragma: no cover + + class StrEnum(str, Enum): # pragma: no cover + pass # pragma: no cover + + +class ControlMode(StrEnum): + scheduled = "Scheduled" + dynamic = "Dynamic" + + +class EnergyTransferMode(StrEnum): + ac_single_phase = "AC_single_phase" + ac_two_phase = "AC_two_phase" + ac_three_phase = "AC_three_phase" + dc = "DC" + ac_single_phase_bpt = "AC_single_phase_BPT" + ac_two_phase_bpt = "AC_two_phase_BPT" + ac_three_phase_bpt = "AC_three_phase_BPT" + dc_bpt = "DC_BPT" + dc_acdp = "DC_ACDP" + dc_acdp_bpt = "DC_ACDP_BPT" + wpt = "WPT" + + +class MobilityNeedsMode(StrEnum): + evcc = "EVCC" + evcc_secc = "EVCC_SECC" + + +class Pricing(StrEnum): + no_pricing = "NoPricing" + absolute_pricing = "AbsolutePricing" + price_levels = "PriceLevels" diff --git a/scripts/v21/tests/test_code_generator.py b/scripts/v21/tests/test_code_generator.py new file mode 100644 index 000000000..59342512e --- /dev/null +++ b/scripts/v21/tests/test_code_generator.py @@ -0,0 +1,98 @@ +""" Some of the tests in this module load schema files from scripts/v21/tests/fixtures, +generate code and compare that code against the 'golden files' in scripts/v21/tests/golden_files. + +Let's say you realize that the code generator contains a bug: the enums generated for schema +TransactionEventRequest are incorrect. To use this schema in the the unit perform the following 2 steps: + +1. Add a copy of the schema to scripts/v21/tests/fixtures/enums/TransactionEventRequest.json. +2. Create a Python file with the expected code output in scripts/v21/tests/golden_files/enums/TransactionEventRequest.py. + +Now run the tests with `poetry run pytest scripts/v21/tests`. +""" +import json +import pathlib +import sys +from typing import List, Tuple + +import pytest + +PARENT_DIR = pathlib.Path(__file__).parent +FIXTURES_DIR = PARENT_DIR / "fixtures" +GOLDEN_FILES_DIR = PARENT_DIR / "golden_files" + +sys.path.append(str(PARENT_DIR.absolute())) + +from code_generator import ( + _camel_to_snake_case, + generate_call_results, + generate_calls, + generate_datatypes, + generate_enums, +) +from json_schema_parser import Object, parse + + +def _load_objects(path: pathlib.Path) -> List[Tuple[str, Object]]: + """Load all schema files at `path` as `Object`s.""" + schema_files = sorted(path.glob("*.json")) + + objects = [] + + for schema_file in schema_files: + name = schema_file.stem + schema = json.load(schema_file.open(encoding="utf-8-sig")) + + object = parse(name.removesuffix("Request").removesuffix("Response"), schema) + + objects.append((name, object)) + + return objects + + +@pytest.mark.parametrize("name, object", _load_objects(FIXTURES_DIR / "enums")) +def test_generate_enums(name: str, object: Object): + code = generate_enums([object]) + compare_code_against_golden_file(code, GOLDEN_FILES_DIR / "enums" / f"{name}.py") + + +@pytest.mark.parametrize("name, object", _load_objects(FIXTURES_DIR / "datatypes")) +def test_generate_datatypes(name: str, object: Object): + code = generate_datatypes([object]) + compare_code_against_golden_file( + code, GOLDEN_FILES_DIR / "datatypes" / f"{name}.py" + ) + + +@pytest.mark.parametrize("name, object", _load_objects(FIXTURES_DIR / "calls")) +def test_generate_calls(name: str, object: Object): + code = generate_calls([object]) + compare_code_against_golden_file(code, GOLDEN_FILES_DIR / "calls" / f"{name}.py") + + +@pytest.mark.parametrize("name, object", _load_objects(FIXTURES_DIR / "call_results")) +def test_generate_call_results(name: str, object: Object): + code = generate_call_results([object]) + compare_code_against_golden_file( + code, GOLDEN_FILES_DIR / "call_results" / f"{name}.py" + ) + + +@pytest.mark.parametrize( + "input, output", + [ + ("evseId", "evse_id"), + ("prioritizedEMAIDs", "prioritized_emaids"), + ("iso15118SchemaVersion", "iso_15118_schema_version"), + ("evMinV2XEnergyRequest", "ev_min_v2x_energy_request"), + ], +) +def test_camel_to_snake_case(input: str, output: str): + assert _camel_to_snake_case(input) == output + + +def compare_code_against_golden_file(code: str, golden_file: pathlib.Path): + """Verify that `code` matches the content of `golden_file`.""" + if not golden_file.exists(): + raise FileNotFoundError(f"Golden file '{golden_file}' doesn't exists") + + assert code == golden_file.open().read()