diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 7d2d7bc..81e0b6c 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -13,82 +13,114 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v4 - - run: git fetch --prune --unshallow --tags + - run: git fetch --prune --unshallow --tags - - run: pipx install poetry + - run: pipx install poetry - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: "3.x" - cache: 'poetry' + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + cache: "poetry" - - run: poetry install + - run: poetry install - - run: poetry self add poetry-version-plugin + - run: poetry self add poetry-version-plugin - - run: poetry build + - run: poetry build - - name: Store the distribution packages - uses: actions/upload-artifact@v4 - with: - name: python-package-distributions - path: dist/ + - name: Store the distribution packages + uses: actions/upload-artifact@v4 + with: + name: python-package-distributions + path: dist/ publish-to-pypi: name: Publish Python 🐍 distribution 📦 to PyPI needs: - - build + - build runs-on: ubuntu-latest environment: name: pypi url: https://pypi.org/p/${{ env.name }} permissions: - id-token: write # IMPORTANT: mandatory for trusted publishing + id-token: write # IMPORTANT: mandatory for trusted publishing steps: - - name: Download all the dists - uses: actions/download-artifact@v4 - with: - name: python-package-distributions - path: dist/ - - name: Publish distribution 📦 to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 github-release: name: >- Sign the Python 🐍 distribution 📦 with Sigstore and upload them to GitHub Release needs: - - publish-to-pypi + - publish-to-pypi runs-on: ubuntu-latest permissions: - contents: write # IMPORTANT: mandatory for making GitHub Releases - id-token: write # IMPORTANT: mandatory for sigstore + contents: write # IMPORTANT: mandatory for making GitHub Releases + id-token: write # IMPORTANT: mandatory for sigstore steps: - - name: Download all the dists - uses: actions/download-artifact@v4 - with: - name: python-package-distributions - path: dist/ - - name: Sign the dists with Sigstore - uses: sigstore/gh-action-sigstore-python@v2.1.1 - with: - inputs: >- - ./dist/*.tar.gz - ./dist/*.whl - - name: Upload artifact signatures to GitHub Release - env: - GITHUB_TOKEN: ${{ github.token }} - # Upload to GitHub Release using the `gh` CLI. - # `dist/` contains the built packages, and the - # sigstore-produced signatures and certificates. - run: >- - gh release upload - '${{ github.ref_name }}' dist/** - --repo '${{ github.repository }}' + - name: Download all the dists + uses: actions/download-artifact@v4 + with: + name: python-package-distributions + path: dist/ + - name: Sign the dists with Sigstore + uses: sigstore/gh-action-sigstore-python@v2.1.1 + with: + inputs: >- + ./dist/*.tar.gz + ./dist/*.whl + - name: Upload artifact signatures to GitHub Release + env: + GITHUB_TOKEN: ${{ github.token }} + # Upload to GitHub Release using the `gh` CLI. + # `dist/` contains the built packages, and the + # sigstore-produced signatures and certificates. + run: >- + gh release upload + '${{ github.ref_name }}' dist/** + --repo '${{ github.repository }}' + + publish-docs: + name: Publish documentation 📚 to GitHub Pages + needs: + - github-release + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - run: git fetch --prune --unshallow --tags + + - run: pipx install poetry + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: "3.x" + cache: "poetry" + + - run: poetry install --only doc + + - name: Configure git for gh-pages + run: | + git config --global user.name "SMP Docs Bot" + git config --global user.email "docs@dummy.bot.com" + + - name: Set release version + run: echo "GIT_TAG=${{ github.event.release.tag_name }}" >> $GITHUB_ENV + + - name: Build and deploy documentation + run: poetry run mike deploy --push --update-aliases ${GIT_TAG} latest diff --git a/.github/workflows/test-docs.yaml b/.github/workflows/test-docs.yaml new file mode 100644 index 0000000..86ffbc4 --- /dev/null +++ b/.github/workflows/test-docs.yaml @@ -0,0 +1,24 @@ +name: Test Docs Build +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + build-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install poetry + run: pipx install poetry + + - uses: actions/setup-python@v5 + with: + python-version: "3.x" + cache: "poetry" + + - run: poetry install --only doc + + - run: poetry run mkdocs build diff --git a/.gitignore b/.gitignore index 17f9ff9..f2ae8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ __pycache__ .pytest_cache dist .tox +site diff --git a/README.md b/README.md index 368d136..a033042 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,11 @@ If you need an SMP CLI application to interact with device firmware, then try `smp` is [distributed by PyPI](https://pypi.org/project/smp/) and can be installed with `poetry`, `pip`, and other dependency managers. +## User Documentation + +Documentation is in the source code so that it is available to your editor. +An online version is generated and available [here](https://jphutchins.github.io/smp/). + ## Development Quickstart > Assumes that you've already [setup your development environment](#development-environment-setup). diff --git a/docs/error.md b/docs/error.md new file mode 100644 index 0000000..245312a --- /dev/null +++ b/docs/error.md @@ -0,0 +1,3 @@ +# SMP V1 and V2 Errors + +::: smp.error \ No newline at end of file diff --git a/docs/exception.md b/docs/exception.md new file mode 100644 index 0000000..f1c5c7c --- /dev/null +++ b/docs/exception.md @@ -0,0 +1,3 @@ +# SMP Exceptions + +::: smp.exceptions \ No newline at end of file diff --git a/docs/file_management.md b/docs/file_management.md new file mode 100644 index 0000000..0107d62 --- /dev/null +++ b/docs/file_management.md @@ -0,0 +1,3 @@ +# File Management + +::: smp.file_management \ No newline at end of file diff --git a/docs/image_management.md b/docs/image_management.md new file mode 100644 index 0000000..2e5ca54 --- /dev/null +++ b/docs/image_management.md @@ -0,0 +1,3 @@ +# Image Management + +::: smp.image_management \ No newline at end of file diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..47e64b5 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,3 @@ +# Simple Management Protocol (SMP) + +::: smp diff --git a/docs/message.md b/docs/message.md new file mode 100644 index 0000000..d3d751a --- /dev/null +++ b/docs/message.md @@ -0,0 +1,5 @@ +# De/serialization and Validation + +::: smp.message + options: + filters: [] \ No newline at end of file diff --git a/docs/os_management.md b/docs/os_management.md new file mode 100644 index 0000000..26f5455 --- /dev/null +++ b/docs/os_management.md @@ -0,0 +1,3 @@ +# OS Management + +::: smp.os_management \ No newline at end of file diff --git a/docs/packet.md b/docs/packet.md new file mode 100644 index 0000000..4fbf5b7 --- /dev/null +++ b/docs/packet.md @@ -0,0 +1,5 @@ +# Packet en/de-coding and fragmentation/reassembly + +::: smp.packet + options: + show_if_no_docstring: false \ No newline at end of file diff --git a/docs/settings_management.md b/docs/settings_management.md new file mode 100644 index 0000000..b404307 --- /dev/null +++ b/docs/settings_management.md @@ -0,0 +1,3 @@ +# Settings Management + +::: smp.settings_management \ No newline at end of file diff --git a/docs/shell_management.md b/docs/shell_management.md new file mode 100644 index 0000000..06e53b1 --- /dev/null +++ b/docs/shell_management.md @@ -0,0 +1,3 @@ +# Shell Management + +::: smp.shell_management \ No newline at end of file diff --git a/docs/statistics_management.md b/docs/statistics_management.md new file mode 100644 index 0000000..b3ad1de --- /dev/null +++ b/docs/statistics_management.md @@ -0,0 +1,3 @@ +# Statistics Management + +::: smp.statistics_management \ No newline at end of file diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css new file mode 100644 index 0000000..47bf222 --- /dev/null +++ b/docs/stylesheets/extra.css @@ -0,0 +1,44 @@ +@import url('https://fonts.googleapis.com/css2?family=DM+Serif+Display:ital@0;1&family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap'); + +:root { + --md-default-bg-color: #f6e8d9; + --md-primary-fg-color: #80A69C; + --md-accent-fg-color: #80A69C; + --ic-dark-teal: #80A69C; + --ic-dark-lime: #BAC452; + --ic-light-lime: #b8c440; + --ic-extra-dark-lime: #939C04; + --ic-light-teal: #80A69C; + --ic-beige: #F6E8D9; + --cream: #f6e8d9; + --black: black; + --white: white; + --grey: grey; + --marin: #93bcb3; + --blue: #9bbdd5; + --purple: #c2aebe; + --yelow: #b8c440; + --pink: #d9b6a3; +} + +.md-header { + color: #000000; + background-color: var(--marin); + font-family: 'Montserrat', sans-serif; +} + +.md-header .md-header__topic { + color: #000000; + font-weight: 700; +} + +.md-typeset h1 { + color: var(--ic-extra-dark-lime); + font-family: 'DM Serif Display', serif; + font-weight: 400; + font-size: 42px; +} + +.md-nav__title { + color: #000000; +} \ No newline at end of file diff --git a/docs/user/intercreate.md b/docs/user/intercreate.md new file mode 100644 index 0000000..9c17684 --- /dev/null +++ b/docs/user/intercreate.md @@ -0,0 +1,3 @@ +# Intercreate + +::: smp.user.intercreate \ No newline at end of file diff --git a/docs/zephyr_management.md b/docs/zephyr_management.md new file mode 100644 index 0000000..dfb220b --- /dev/null +++ b/docs/zephyr_management.md @@ -0,0 +1,3 @@ +# Zephyr Management + +::: smp.zephyr_management \ No newline at end of file diff --git a/mkdocs.yaml b/mkdocs.yaml new file mode 100644 index 0000000..868dbab --- /dev/null +++ b/mkdocs.yaml @@ -0,0 +1,71 @@ +site_name: Simple Management Protocol (SMP) + +repo_name: JPHutchins/smp +repo_url: https://github.com/JPHutchins/smp + +nav: + - Home: index.md + - File Management: file_management.md + - Image Management: image_management.md + - OS Management: os_management.md + - Settings Management: settings_management.md + - Shell Management: shell_management.md + - Statistics Management: statistics_management.md + - Zephyr Management: zephyr_management.md + - User Groups: + - Intercreate: user/intercreate.md + - Packets (USB/Serial): packet.md + - Errors: error.md + - Exceptions: exception.md + - De/Serialization: message.md + +plugins: + - search + - mkdocstrings: + handlers: + python: + options: + docstring_style: google + show_source: true + members_order: source + show_private_members: true + show_signature_annotations: true + show_if_no_docstring: true + - mike: + # These fields are all optional; the defaults are as below... + alias_type: symlink + redirect_template: null + deploy_prefix: "" + canonical_version: null + version_selector: true + css_dir: css + javascript_dir: js + +extra: + version: + provider: mike + +extra_css: + - stylesheets/extra.css + +theme: + name: material + palette: + primary: custom + accent: custom + +markdown_extensions: + - admonition + - codehilite + - footnotes + - meta + - toc: + permalink: true + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.magiclink + - pymdownx.snippets + - pymdownx.superfences diff --git a/poetry.lock b/poetry.lock index 39214aa..ebcd6df 100644 --- a/poetry.lock +++ b/poetry.lock @@ -14,6 +14,38 @@ files = [ [package.dependencies] typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} +[[package]] +name = "astunparse" +version = "1.6.3" +description = "An AST unparser for Python" +optional = false +python-versions = "*" +files = [ + {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, + {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +] + +[package.dependencies] +six = ">=1.6.1,<2.0" +wheel = ">=0.23.0,<1.0" + +[[package]] +name = "babel" +version = "2.16.0" +description = "Internationalization utilities" +optional = false +python-versions = ">=3.8" +files = [ + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, +] + +[package.dependencies] +pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} + +[package.extras] +dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] + [[package]] name = "black" version = "23.12.1" @@ -122,6 +154,17 @@ benchmarks = ["pytest-benchmark (==4.0.0)"] doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.3.0)", "typing-extensions"] test = ["coverage (>=7)", "hypothesis", "pytest"] +[[package]] +name = "certifi" +version = "2024.8.30" +description = "Python package for providing Mozilla's CA Bundle." +optional = false +python-versions = ">=3.6" +files = [ + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, +] + [[package]] name = "chardet" version = "5.2.0" @@ -133,6 +176,105 @@ files = [ {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, ] +[[package]] +name = "charset-normalizer" +version = "3.3.2" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, +] + [[package]] name = "click" version = "8.1.7" @@ -340,6 +482,97 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.11.0,<2.12.0" pyflakes = ">=3.1.0,<3.2.0" +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +optional = false +python-versions = "*" +files = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["flake8", "markdown", "twine", "wheel"] + +[[package]] +name = "griffe" +version = "1.3.1" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +optional = false +python-versions = ">=3.8" +files = [ + {file = "griffe-1.3.1-py3-none-any.whl", hash = "sha256:940aeb630bc3054b4369567f150b6365be6f11eef46b0ed8623aea96e6d17b19"}, + {file = "griffe-1.3.1.tar.gz", hash = "sha256:3f86a716b631a4c0f96a43cb75d05d3c85975003c20540426c0eba3b0581c56a"}, +] + +[package.dependencies] +astunparse = {version = ">=1.6", markers = "python_version < \"3.9\""} +colorama = ">=0.4" + +[[package]] +name = "idna" +version = "3.10" +description = "Internationalized Domain Names in Applications (IDNA)" +optional = false +python-versions = ">=3.6" +files = [ + {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, + {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, +] + +[package.extras] +all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2)"] + +[[package]] +name = "importlib-metadata" +version = "8.5.0" +description = "Read metadata from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-8.5.0-py3-none-any.whl", hash = "sha256:45e54197d28b7a7f1559e60b95e7c567032b602131fbd588f1497f47880aa68b"}, + {file = "importlib_metadata-8.5.0.tar.gz", hash = "sha256:71522656f0abace1d072b9e5481a48f07c138e00f079c38c8f883823f9c26bd7"}, +] + +[package.dependencies] +zipp = ">=3.20" + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +perf = ["ipython"] +test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "packaging", "pyfakefs", "pytest (>=6,!=8.1.*)", "pytest-perf (>=0.9.2)"] +type = ["pytest-mypy"] + +[[package]] +name = "importlib-resources" +version = "6.4.5" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"}, + {file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"}, +] + +[package.dependencies] +zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] + [[package]] name = "iniconfig" version = "2.0.0" @@ -365,6 +598,110 @@ files = [ [package.extras] colors = ["colorama (>=0.4.6)"] +[[package]] +name = "jinja2" +version = "3.1.4" +description = "A very fast and expressive template engine." +optional = false +python-versions = ">=3.7" +files = [ + {file = "jinja2-3.1.4-py3-none-any.whl", hash = "sha256:bc5dd2abb727a5319567b7a813e6a2e7318c39f4f487cfe6c89c6f9c7d25197d"}, + {file = "jinja2-3.1.4.tar.gz", hash = "sha256:4a3aee7acbbe7303aede8e9648d13b8bf88a429282aa6122a993f0ac800cb369"}, +] + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markdown" +version = "3.7" +description = "Python implementation of John Gruber's Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, + {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.5" +description = "Safely add untrusted strings to HTML/XML markup." +optional = false +python-versions = ">=3.7" +files = [ + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, + {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, + {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, + {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, + {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, + {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, + {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, + {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, +] + [[package]] name = "mccabe" version = "0.7.0" @@ -376,6 +713,191 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +optional = false +python-versions = ">=3.6" +files = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] + +[[package]] +name = "mike" +version = "2.1.3" +description = "Manage multiple versions of your MkDocs-powered documentation" +optional = false +python-versions = "*" +files = [ + {file = "mike-2.1.3-py3-none-any.whl", hash = "sha256:d90c64077e84f06272437b464735130d380703a76a5738b152932884c60c062a"}, + {file = "mike-2.1.3.tar.gz", hash = "sha256:abd79b8ea483fb0275b7972825d3082e5ae67a41820f8d8a0dc7a3f49944e810"}, +] + +[package.dependencies] +importlib-metadata = "*" +importlib-resources = "*" +jinja2 = ">=2.7" +mkdocs = ">=1.0" +pyparsing = ">=3.0" +pyyaml = ">=5.1" +pyyaml-env-tag = "*" +verspec = "*" + +[package.extras] +dev = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] +test = ["coverage", "flake8 (>=3.0)", "flake8-quotes", "shtab"] + +[[package]] +name = "mkdocs" +version = "1.6.1" +description = "Project documentation with Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, +] + +[package.dependencies] +click = ">=7.0" +colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} +ghp-import = ">=1.0" +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} +jinja2 = ">=2.11.1" +markdown = ">=3.3.6" +markupsafe = ">=2.0.1" +mergedeep = ">=1.3.4" +mkdocs-get-deps = ">=0.2.0" +packaging = ">=20.5" +pathspec = ">=0.11.1" +pyyaml = ">=5.1" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] +min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.4)", "jinja2 (==2.11.1)", "markdown (==3.3.6)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "mkdocs-get-deps (==0.2.0)", "packaging (==20.5)", "pathspec (==0.11.1)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "watchdog (==2.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "1.2.0" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_autorefs-1.2.0-py3-none-any.whl", hash = "sha256:d588754ae89bd0ced0c70c06f58566a4ee43471eeeee5202427da7de9ef85a2f"}, + {file = "mkdocs_autorefs-1.2.0.tar.gz", hash = "sha256:a86b93abff653521bda71cf3fc5596342b7a23982093915cb74273f67522190f"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +description = "MkDocs extension that lists all dependencies according to a mkdocs.yml file" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134"}, + {file = "mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c"}, +] + +[package.dependencies] +importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} +mergedeep = ">=1.3.4" +platformdirs = ">=2.2.0" +pyyaml = ">=5.1" + +[[package]] +name = "mkdocs-material" +version = "9.5.38" +description = "Documentation that simply works" +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material-9.5.38-py3-none-any.whl", hash = "sha256:d4779051d52ba9f1e7e344b34de95449c7c366c212b388e4a2db9a3db043c228"}, + {file = "mkdocs_material-9.5.38.tar.gz", hash = "sha256:1843c5171ad6b489550aeaf7358e5b7128cc03ddcf0fb4d91d19aa1e691a63b8"}, +] + +[package.dependencies] +babel = ">=2.10,<3.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.0,<4.0" +markdown = ">=3.2,<4.0" +mkdocs = ">=1.6,<2.0" +mkdocs-material-extensions = ">=1.3,<2.0" +paginate = ">=0.5,<1.0" +pygments = ">=2.16,<3.0" +pymdown-extensions = ">=10.2,<11.0" +regex = ">=2022.4" +requests = ">=2.26,<3.0" + +[package.extras] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] +imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] +recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, + {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, +] + +[[package]] +name = "mkdocstrings" +version = "0.26.1" +description = "Automatic documentation from sources, for MkDocs." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings-0.26.1-py3-none-any.whl", hash = "sha256:29738bfb72b4608e8e55cc50fb8a54f325dc7ebd2014e4e3881a49892d5983cf"}, + {file = "mkdocstrings-0.26.1.tar.gz", hash = "sha256:bb8b8854d6713d5348ad05b069a09f3b79edbc6a0f33a34c6821141adb03fe33"}, +] + +[package.dependencies] +click = ">=7.0" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +Jinja2 = ">=2.11.1" +Markdown = ">=3.6" +MarkupSafe = ">=1.1" +mkdocs = ">=1.4" +mkdocs-autorefs = ">=1.2" +mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""} +platformdirs = ">=2.2" +pymdown-extensions = ">=6.3" +typing-extensions = {version = ">=4.1", markers = "python_version < \"3.10\""} + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=0.5.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "1.11.1" +description = "A Python handler for mkdocstrings." +optional = false +python-versions = ">=3.8" +files = [ + {file = "mkdocstrings_python-1.11.1-py3-none-any.whl", hash = "sha256:a21a1c05acef129a618517bb5aae3e33114f569b11588b1e7af3e9d4061a71af"}, + {file = "mkdocstrings_python-1.11.1.tar.gz", hash = "sha256:8824b115c5359304ab0b5378a91f6202324a849e1da907a3485b59208b797322"}, +] + +[package.dependencies] +griffe = ">=0.49" +mkdocs-autorefs = ">=1.2" +mkdocstrings = ">=0.26" + [[package]] name = "mypy" version = "1.11.2" @@ -445,6 +967,21 @@ files = [ {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] +[[package]] +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" +optional = false +python-versions = "*" +files = [ + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, +] + +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + [[package]] name = "pathspec" version = "0.12.1" @@ -633,15 +1170,61 @@ files = [ {file = "pyflakes-3.1.0.tar.gz", hash = "sha256:a0aae034c444db0071aa077972ba4768d40c830d9539fd45bf4cd3f8f6992efc"}, ] +[[package]] +name = "pygments" +version = "2.18.0" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"}, + {file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"}, +] + +[package.extras] +windows-terminal = ["colorama (>=0.4.6)"] + +[[package]] +name = "pymdown-extensions" +version = "10.11" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pymdown_extensions-10.11-py3-none-any.whl", hash = "sha256:e68080eac44634406b31f4aec58fbad17b0ec5fca6b086e29008616d54c3906b"}, + {file = "pymdown_extensions-10.11.tar.gz", hash = "sha256:2653fb658bca5f278029f8c67a67f0f08b7bd3c657e2630d261ad542e97c4192"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.12)"] + +[[package]] +name = "pyparsing" +version = "3.1.4" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pyproject-api" -version = "1.7.2" +version = "1.8.0" description = "API to interact with the python pyproject.toml based projects" optional = false python-versions = ">=3.8" files = [ - {file = "pyproject_api-1.7.2-py3-none-any.whl", hash = "sha256:17c025105f8d27e22ffe542fe7dff3391b3736191a28294773a1f3b9ed25282b"}, - {file = "pyproject_api-1.7.2.tar.gz", hash = "sha256:dc5b0e0f6e291a4f22b46e182c9c6d4915c62b1f089b8de1b73f2d06ae453593"}, + {file = "pyproject_api-1.8.0-py3-none-any.whl", hash = "sha256:3d7d347a047afe796fd5d1885b1e391ba29be7169bd2f102fcd378f04273d228"}, + {file = "pyproject_api-1.8.0.tar.gz", hash = "sha256:77b8049f2feb5d33eefcc21b57f1e279636277a8ac8ad6b5871037b243778496"}, ] [package.dependencies] @@ -712,6 +1295,242 @@ psutil = ["psutil (>=3.0)"] setproctitle = ["setproctitle"] testing = ["filelock"] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +description = "Extensions to the standard Python datetime module" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +files = [ + {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, + {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, +] + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytz" +version = "2024.2" +description = "World timezone definitions, modern and historical" +optional = false +python-versions = "*" +files = [ + {file = "pytz-2024.2-py2.py3-none-any.whl", hash = "sha256:31c7c1817eb7fae7ca4b8c7ee50c72f93aa2dd863de768e1ef4245d426aa0725"}, + {file = "pytz-2024.2.tar.gz", hash = "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a"}, +] + +[[package]] +name = "pyyaml" +version = "6.0.2" +description = "YAML parser and emitter for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, +] + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +optional = false +python-versions = ">=3.6" +files = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "regex" +version = "2024.9.11" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.8" +files = [ + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1494fa8725c285a81d01dc8c06b55287a1ee5e0e382d8413adc0a9197aac6408"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0e12c481ad92d129c78f13a2a3662317e46ee7ef96c94fd332e1c29131875b7d"}, + {file = "regex-2024.9.11-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:16e13a7929791ac1216afde26f712802e3df7bf0360b32e4914dca3ab8baeea5"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46989629904bad940bbec2106528140a218b4a36bb3042d8406980be1941429c"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a906ed5e47a0ce5f04b2c981af1c9acf9e8696066900bf03b9d7879a6f679fc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e9a091b0550b3b0207784a7d6d0f1a00d1d1c8a11699c1a4d93db3fbefc3ad35"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ddcd9a179c0a6fa8add279a4444015acddcd7f232a49071ae57fa6e278f1f71"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6b41e1adc61fa347662b09398e31ad446afadff932a24807d3ceb955ed865cc8"}, + {file = "regex-2024.9.11-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ced479f601cd2f8ca1fd7b23925a7e0ad512a56d6e9476f79b8f381d9d37090a"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:635a1d96665f84b292e401c3d62775851aedc31d4f8784117b3c68c4fcd4118d"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c0256beda696edcf7d97ef16b2a33a8e5a875affd6fa6567b54f7c577b30a137"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:3ce4f1185db3fbde8ed8aa223fc9620f276c58de8b0d4f8cc86fd1360829edb6"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:09d77559e80dcc9d24570da3745ab859a9cf91953062e4ab126ba9d5993688ca"}, + {file = "regex-2024.9.11-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:7a22ccefd4db3f12b526eccb129390942fe874a3a9fdbdd24cf55773a1faab1a"}, + {file = "regex-2024.9.11-cp310-cp310-win32.whl", hash = "sha256:f745ec09bc1b0bd15cfc73df6fa4f726dcc26bb16c23a03f9e3367d357eeedd0"}, + {file = "regex-2024.9.11-cp310-cp310-win_amd64.whl", hash = "sha256:01c2acb51f8a7d6494c8c5eafe3d8e06d76563d8a8a4643b37e9b2dd8a2ff623"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2cce2449e5927a0bf084d346da6cd5eb016b2beca10d0013ab50e3c226ffc0df"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3b37fa423beefa44919e009745ccbf353d8c981516e807995b2bd11c2c77d268"}, + {file = "regex-2024.9.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64ce2799bd75039b480cc0360907c4fb2f50022f030bf9e7a8705b636e408fad"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a4cc92bb6db56ab0c1cbd17294e14f5e9224f0cc6521167ef388332604e92679"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d05ac6fa06959c4172eccd99a222e1fbf17b5670c4d596cb1e5cde99600674c4"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:040562757795eeea356394a7fb13076ad4f99d3c62ab0f8bdfb21f99a1f85664"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6113c008a7780792efc80f9dfe10ba0cd043cbf8dc9a76ef757850f51b4edc50"}, + {file = "regex-2024.9.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8e5fb5f77c8745a60105403a774fe2c1759b71d3e7b4ca237a5e67ad066c7199"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:54d9ff35d4515debf14bc27f1e3b38bfc453eff3220f5bce159642fa762fe5d4"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:df5cbb1fbc74a8305b6065d4ade43b993be03dbe0f8b30032cced0d7740994bd"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:7fb89ee5d106e4a7a51bce305ac4efb981536301895f7bdcf93ec92ae0d91c7f"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:a738b937d512b30bf75995c0159c0ddf9eec0775c9d72ac0202076c72f24aa96"}, + {file = "regex-2024.9.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e28f9faeb14b6f23ac55bfbbfd3643f5c7c18ede093977f1df249f73fd22c7b1"}, + {file = "regex-2024.9.11-cp311-cp311-win32.whl", hash = "sha256:18e707ce6c92d7282dfce370cd205098384b8ee21544e7cb29b8aab955b66fa9"}, + {file = "regex-2024.9.11-cp311-cp311-win_amd64.whl", hash = "sha256:313ea15e5ff2a8cbbad96ccef6be638393041b0a7863183c2d31e0c6116688cf"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:b0d0a6c64fcc4ef9c69bd5b3b3626cc3776520a1637d8abaa62b9edc147a58f7"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:49b0e06786ea663f933f3710a51e9385ce0cba0ea56b67107fd841a55d56a231"}, + {file = "regex-2024.9.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5b513b6997a0b2f10e4fd3a1313568e373926e8c252bd76c960f96fd039cd28d"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee439691d8c23e76f9802c42a95cfeebf9d47cf4ffd06f18489122dbb0a7ad64"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8f877c89719d759e52783f7fe6e1c67121076b87b40542966c02de5503ace42"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23b30c62d0f16827f2ae9f2bb87619bc4fba2044911e2e6c2eb1af0161cdb766"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85ab7824093d8f10d44330fe1e6493f756f252d145323dd17ab6b48733ff6c0a"}, + {file = "regex-2024.9.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8dee5b4810a89447151999428fe096977346cf2f29f4d5e29609d2e19e0199c9"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98eeee2f2e63edae2181c886d7911ce502e1292794f4c5ee71e60e23e8d26b5d"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:57fdd2e0b2694ce6fc2e5ccf189789c3e2962916fb38779d3e3521ff8fe7a822"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:d552c78411f60b1fdaafd117a1fca2f02e562e309223b9d44b7de8be451ec5e0"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:a0b2b80321c2ed3fcf0385ec9e51a12253c50f146fddb2abbb10f033fe3d049a"}, + {file = "regex-2024.9.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:18406efb2f5a0e57e3a5881cd9354c1512d3bb4f5c45d96d110a66114d84d23a"}, + {file = "regex-2024.9.11-cp312-cp312-win32.whl", hash = "sha256:e464b467f1588e2c42d26814231edecbcfe77f5ac414d92cbf4e7b55b2c2a776"}, + {file = "regex-2024.9.11-cp312-cp312-win_amd64.whl", hash = "sha256:9e8719792ca63c6b8340380352c24dcb8cd7ec49dae36e963742a275dfae6009"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:c157bb447303070f256e084668b702073db99bbb61d44f85d811025fcf38f784"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4db21ece84dfeefc5d8a3863f101995de646c6cb0536952c321a2650aa202c36"}, + {file = "regex-2024.9.11-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:220e92a30b426daf23bb67a7962900ed4613589bab80382be09b48896d211e92"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eb1ae19e64c14c7ec1995f40bd932448713d3c73509e82d8cd7744dc00e29e86"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f47cd43a5bfa48f86925fe26fbdd0a488ff15b62468abb5d2a1e092a4fb10e85"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d4a76b96f398697fe01117093613166e6aa8195d63f1b4ec3f21ab637632963"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ea51dcc0835eea2ea31d66456210a4e01a076d820e9039b04ae8d17ac11dee6"}, + {file = "regex-2024.9.11-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7aaa315101c6567a9a45d2839322c51c8d6e81f67683d529512f5bcfb99c802"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c57d08ad67aba97af57a7263c2d9006d5c404d721c5f7542f077f109ec2a4a29"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8404bf61298bb6f8224bb9176c1424548ee1181130818fcd2cbffddc768bed8"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:dd4490a33eb909ef5078ab20f5f000087afa2a4daa27b4c072ccb3cb3050ad84"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:eee9130eaad130649fd73e5cd92f60e55708952260ede70da64de420cdcad554"}, + {file = "regex-2024.9.11-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a2644a93da36c784e546de579ec1806bfd2763ef47babc1b03d765fe560c9f8"}, + {file = "regex-2024.9.11-cp313-cp313-win32.whl", hash = "sha256:e997fd30430c57138adc06bba4c7c2968fb13d101e57dd5bb9355bf8ce3fa7e8"}, + {file = "regex-2024.9.11-cp313-cp313-win_amd64.whl", hash = "sha256:042c55879cfeb21a8adacc84ea347721d3d83a159da6acdf1116859e2427c43f"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:35f4a6f96aa6cb3f2f7247027b07b15a374f0d5b912c0001418d1d55024d5cb4"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:55b96e7ce3a69a8449a66984c268062fbaa0d8ae437b285428e12797baefce7e"}, + {file = "regex-2024.9.11-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cb130fccd1a37ed894824b8c046321540263013da72745d755f2d35114b81a60"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:323c1f04be6b2968944d730e5c2091c8c89767903ecaa135203eec4565ed2b2b"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be1c8ed48c4c4065ecb19d882a0ce1afe0745dfad8ce48c49586b90a55f02366"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b5b029322e6e7b94fff16cd120ab35a253236a5f99a79fb04fda7ae71ca20ae8"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6fff13ef6b5f29221d6904aa816c34701462956aa72a77f1f151a8ec4f56aeb"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:587d4af3979376652010e400accc30404e6c16b7df574048ab1f581af82065e4"}, + {file = "regex-2024.9.11-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:079400a8269544b955ffa9e31f186f01d96829110a3bf79dc338e9910f794fca"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:f9268774428ec173654985ce55fc6caf4c6d11ade0f6f914d48ef4719eb05ebb"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:23f9985c8784e544d53fc2930fc1ac1a7319f5d5332d228437acc9f418f2f168"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:ae2941333154baff9838e88aa71c1d84f4438189ecc6021a12c7573728b5838e"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:e93f1c331ca8e86fe877a48ad64e77882c0c4da0097f2212873a69bbfea95d0c"}, + {file = "regex-2024.9.11-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:846bc79ee753acf93aef4184c040d709940c9d001029ceb7b7a52747b80ed2dd"}, + {file = "regex-2024.9.11-cp38-cp38-win32.whl", hash = "sha256:c94bb0a9f1db10a1d16c00880bdebd5f9faf267273b8f5bd1878126e0fbde771"}, + {file = "regex-2024.9.11-cp38-cp38-win_amd64.whl", hash = "sha256:2b08fce89fbd45664d3df6ad93e554b6c16933ffa9d55cb7e01182baaf971508"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:07f45f287469039ffc2c53caf6803cd506eb5f5f637f1d4acb37a738f71dd066"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4838e24ee015101d9f901988001038f7f0d90dc0c3b115541a1365fb439add62"}, + {file = "regex-2024.9.11-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6edd623bae6a737f10ce853ea076f56f507fd7726bee96a41ee3d68d347e4d16"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c69ada171c2d0e97a4b5aa78fbb835e0ffbb6b13fc5da968c09811346564f0d3"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:02087ea0a03b4af1ed6ebab2c54d7118127fee8d71b26398e8e4b05b78963199"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69dee6a020693d12a3cf892aba4808fe168d2a4cef368eb9bf74f5398bfd4ee8"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:297f54910247508e6e5cae669f2bc308985c60540a4edd1c77203ef19bfa63ca"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ecea58b43a67b1b79805f1a0255730edaf5191ecef84dbc4cc85eb30bc8b63b9"}, + {file = "regex-2024.9.11-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eab4bb380f15e189d1313195b062a6aa908f5bd687a0ceccd47c8211e9cf0d4a"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0cbff728659ce4bbf4c30b2a1be040faafaa9eca6ecde40aaff86f7889f4ab39"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:54c4a097b8bc5bb0dfc83ae498061d53ad7b5762e00f4adaa23bee22b012e6ba"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:73d6d2f64f4d894c96626a75578b0bf7d9e56dcda8c3d037a2118fdfe9b1c664"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:e53b5fbab5d675aec9f0c501274c467c0f9a5d23696cfc94247e1fb56501ed89"}, + {file = "regex-2024.9.11-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0ffbcf9221e04502fc35e54d1ce9567541979c3fdfb93d2c554f0ca583a19b35"}, + {file = "regex-2024.9.11-cp39-cp39-win32.whl", hash = "sha256:e4c22e1ac1f1ec1e09f72e6c44d8f2244173db7eb9629cc3a346a8d7ccc31142"}, + {file = "regex-2024.9.11-cp39-cp39-win_amd64.whl", hash = "sha256:faa3c142464efec496967359ca99696c896c591c56c53506bac1ad465f66e919"}, + {file = "regex-2024.9.11.tar.gz", hash = "sha256:6c188c307e8433bcb63dc1915022deb553b4203a70722fc542c363bf120a01fd"}, +] + +[[package]] +name = "requests" +version = "2.32.3" +description = "Python HTTP for Humans." +optional = false +python-versions = ">=3.8" +files = [ + {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, + {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, +] + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<4" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<3" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +files = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] + [[package]] name = "tomli" version = "2.0.1" @@ -725,13 +1544,13 @@ files = [ [[package]] name = "tox" -version = "4.19.0" +version = "4.20.0" description = "tox is a generic virtualenv management and test command line tool" optional = false python-versions = ">=3.8" files = [ - {file = "tox-4.19.0-py3-none-any.whl", hash = "sha256:6e20a520db7710f6980b8ec96bde189d6b8cf41b327ec703b03e1a2a447b1aaf"}, - {file = "tox-4.19.0.tar.gz", hash = "sha256:66177d887f9d7ef8eaa9b58b187f7b865fa4c58650086c01336e82c9831e1867"}, + {file = "tox-4.20.0-py3-none-any.whl", hash = "sha256:21a8005e3d3fe5658a8e36b8ca3ed13a4230429063c5cc2a2fdac6ee5aa0de34"}, + {file = "tox-4.20.0.tar.gz", hash = "sha256:5b78a49b6eaaeab3ae4186415e7c97d524f762ae967c63562687c3e5f0ec23d5"}, ] [package.dependencies] @@ -761,15 +1580,46 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] +[[package]] +name = "urllib3" +version = "2.2.3" +description = "HTTP library with thread-safe connection pooling, file post, and more." +optional = false +python-versions = ">=3.8" +files = [ + {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, + {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, +] + +[package.extras] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] +socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] +zstd = ["zstandard (>=0.18.0)"] + +[[package]] +name = "verspec" +version = "0.1.0" +description = "Flexible version handling" +optional = false +python-versions = "*" +files = [ + {file = "verspec-0.1.0-py3-none-any.whl", hash = "sha256:741877d5633cc9464c45a469ae2a31e801e6dbbaa85b9675d481cda100f11c31"}, + {file = "verspec-0.1.0.tar.gz", hash = "sha256:c4504ca697b2056cdb4bfa7121461f5a0e81809255b41c03dda4ba823637c01e"}, +] + +[package.extras] +test = ["coverage", "flake8 (>=3.7)", "mypy", "pretend", "pytest"] + [[package]] name = "virtualenv" -version = "20.26.5" +version = "20.26.6" description = "Virtual Python Environment builder" optional = false python-versions = ">=3.7" files = [ - {file = "virtualenv-20.26.5-py3-none-any.whl", hash = "sha256:4f3ac17b81fba3ce3bd6f4ead2749a72da5929c01774948e243db9ba41df4ff6"}, - {file = "virtualenv-20.26.5.tar.gz", hash = "sha256:ce489cac131aa58f4b25e321d6d186171f78e6cb13fafbf32a840cee67733ff4"}, + {file = "virtualenv-20.26.6-py3-none-any.whl", hash = "sha256:7345cc5b25405607a624d8418154577459c3e0277f5466dd79c49d5e492995f2"}, + {file = "virtualenv-20.26.6.tar.gz", hash = "sha256:280aede09a2a5c317e409a00102e7077c6432c5a38f0ef938e643805a7ad2c48"}, ] [package.dependencies] @@ -781,7 +1631,87 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2,!=7.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "watchdog" +version = "4.0.2" +description = "Filesystem events monitoring" +optional = false +python-versions = ">=3.8" +files = [ + {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, + {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, + {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, + {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, + {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, + {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, + {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, + {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, + {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, + {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, + {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, + {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, + {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, + {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, + {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, + {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, + {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, + {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, + {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, +] + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "wheel" +version = "0.44.0" +description = "A built-package format for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "wheel-0.44.0-py3-none-any.whl", hash = "sha256:2376a90c98cc337d18623527a97c31797bd02bad0033d41547043a1cbfbe448f"}, + {file = "wheel-0.44.0.tar.gz", hash = "sha256:a29c3f2817e95ab89aa4660681ad547c0e9547f20e75b0562fe7723c9a2a9d49"}, +] + +[package.extras] +test = ["pytest (>=6.0.0)", "setuptools (>=65)"] + +[[package]] +name = "zipp" +version = "3.20.2" +description = "Backport of pathlib-compatible object wrapper for zip files" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, + {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] + [metadata] lock-version = "2.0" python-versions = ">=3.8.1, <3.14" -content-hash = "97f29b5e270d34673b761664c8ecd952037fe2aedb5fae96c72f4c65b4977375" +content-hash = "6c742817fcc365ca42b9b5991aba0a2338e58b24142b81b8bbd720e0c13dc3be" diff --git a/pyproject.toml b/pyproject.toml index 06ab8ce..aab7e59 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,6 +9,7 @@ authors = [ readme = "README.md" license = "Apache-2.0" repository = "https://github.com/JPHutchins/smp" +documentation = "https://jphutchins.github.io/smp/" packages = [{ include = "smp" }] [tool.poetry-version-plugin] @@ -32,6 +33,11 @@ mypy-extensions = "^1.0.0" pytest-xdist = "^3.4.0" tox = "^4.15.0" +[tool.poetry.group.doc.dependencies] +mkdocstrings = { extras = ["python"], version = "^0.26.1" } +mkdocs-material = "^9.5.37" +mike = "^2.1.3" + [tool.black] line-length = 100 skip-string-normalization = true diff --git a/smp/__init__.py b/smp/__init__.py index e69de29..5184a10 100644 --- a/smp/__init__.py +++ b/smp/__init__.py @@ -0,0 +1,97 @@ +# flake8: noqa + +"""The Simple Management Protocol (SMP) for remotely managing MCU firmware. + +This package implements de/serialization of SMP messages allowing for their use +on the transport of your choice. + +The SMP specification can be found [here](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_protocol.html). + +## Usage + +SMP messages are represented as Pydantic models. Each SMP Request and +Response contains a `header` attribute that is an instance of +`smp.header.Header`. Other attributes are specific to the message type. + +For example, to create a `smp.os_management.EchoWriteRequest`: + +```python +from smp.os_management import EchoWriteRequest + +request = EchoWriteRequest(d="Hello world!") +print(bytes(request)) +``` + +Prints the serialized SMP Frame: +```python +b'\\n\\x00\\x00\\x10\\x00\\x00\\x00\\x00\\xa1adlHello world!' +``` + +All messages can be deserialized and validated using the `loads()` method. To +load a `smp.os_management.EchoWriteResponse`: + +```python +from smp.os_management import EchoWriteResponse + +data = bytes.fromhex("0b00000700008c00a1617263486921") # data from the transport +response = EchoWriteResponse.loads(data) +print(response) +``` + +Prints the deserialized SMP message representation: +``` +header=Header(op=, version=, flags=, +length=7, group_id=0, sequence=140, command_id=0) version= +sequence=140 smp_data=b'\\x0b\\x00\\x00\\x07\\x00\\x00\\x8c\\x00\\xa1arcHi!' r='Hi!' +``` +Generally, the `header` can be ignored and the message-specific attributes are +what you are interested in. +``` +# print(response.r) +Hi! +``` +All models and their attributes are statically typed and validated; enforced by +mypy linting and by Pydantic at runtime. + +## Serialization + +There are a few optional arguments that are common to all SMP messages when they +are created. + +- `header` is the SMP header. Typically this can be left as `None` and will be + so that the header is created automatically. +- `version` is the SMP version. This defaults to `smp.header.Version.V2`. +- `sequence` is the sequence number of the message. If not provided, it will + be automatically generated using an incrementing counter. + +Take a look at `smp.message` for more information on the base classes. + +## Deserialization + +If you are writing an SMP client, then you already know the type of the +incoming message because it must be a Response to your Request, or an +`smp.error.ErrorV1` or `smp.error.ErrorV2`. You can use the +`smp.message._MessageBase.loads()` method that is common to all SMP +messages to deserialize and validate the message. + +If you are writing an SMP server, then Python and SMP are odd choices! Yet, you +can narrow the type by first loading the header with `smp.header.Header.loads()`. + +## Encoding & Decoding + +The USB and serial transports defined by Zephyr use a base64 encoding and +framing. The encoding/fragmentation and decoding/reassembly is provided by +`smp.packet.encode()` and `smp.packet.decode()`. + +More information is at the [Zephyr docs](https://docs.zephyrproject.org/latest/services/device_mgmt/smp_transport.html#uart-serial-and-console) + +## Typing + +This package is meticulously typed and is intended to be used with mypy. + +## Validation + +All models are validated in order to detect transport and SMP server errors. It +is impossible to create an invalid SMP message or deserialize an invalid SMP +message. If you find a way, please open an issue. +""" diff --git a/smp/error.py b/smp/error.py index 1c16d49..8a29ac6 100644 --- a/smp/error.py +++ b/smp/error.py @@ -64,6 +64,8 @@ class MGMT_ERR(IntEnum): class ErrorV1(message.Response): + """SMP error response version 1.""" + RESPONSE_TYPE = message.ResponseType.ERROR_V1 rc: MGMT_ERR @@ -74,6 +76,8 @@ class ErrorV1(message.Response): class Err(BaseModel, Generic[T]): + """SMP error response version 2 `err` map.""" + model_config = ConfigDict(extra="forbid", frozen=True, arbitrary_types_allowed=True) group: GroupId @@ -81,6 +85,8 @@ class Err(BaseModel, Generic[T]): class ErrorV2(message.Response, Generic[T]): + """SMP error response version 2.""" + RESPONSE_TYPE = message.ResponseType.ERROR_V2 err: Err[T] diff --git a/smp/exceptions.py b/smp/exceptions.py index 7e3722b..7555939 100644 --- a/smp/exceptions.py +++ b/smp/exceptions.py @@ -2,32 +2,32 @@ class SMPException(Exception): - ... + """Base class for SMP exceptions.""" class SMPDecodeError(SMPException): - ... + """An error occurred while decoding an SMP message.""" class SMPBadStartDelimiter(SMPDecodeError): - ... + """The start delimiter of the SMP message is invalid.""" class SMPBadContinueDelimiter(SMPDecodeError): - ... + """The continue delimiter of the SMP message is invalid.""" class SMPBadCRC(SMPDecodeError): - ... + """The CRC of the SMP message failed.""" class SMPDeserializationError(SMPException): - ... + """Failed to deserialize an SMP message.""" class SMPMismatchedGroupId(SMPDeserializationError): - ... + """Group ID in the SMP message does not match the expected group.""" class SMPMalformed(SMPException): - ... + """The SMP message is malformed.""" diff --git a/smp/file_management.py b/smp/file_management.py index 5d8b229..d546130 100644 --- a/smp/file_management.py +++ b/smp/file_management.py @@ -2,8 +2,8 @@ from __future__ import annotations -from enum import IntEnum, auto, unique -from typing import Dict +from enum import IntEnum, unique +from typing import Dict, Literal from pydantic import BaseModel, ConfigDict @@ -11,40 +11,87 @@ class FileDownloadRequest(message.ReadRequest): + """Download contents of an existing file from specified path. + + Client applications must keep track of data they have already downloaded and + where their position in the file is, and issue subsequent requests, with + modified offset, to gather the entire file. + + Request does not carry size of requested chunk, the size is specified by + application itself. Note that file handles will remain open for consecutive + requests (as long as an idle timeout has not been reached and another + transport does not make use of uploading/downloading files using fs_mgmt), + but files are not exclusively owned by SMP, for the time of download + session, and may change between requests or even be removed. + """ + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_DOWNLOAD_UPLOAD off: int + """Offset in the file to read from.""" name: str + """Name of the file to download.""" class FileDownloadResponse(message.ReadResponse): + """Success response to a file download request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_DOWNLOAD_UPLOAD off: int + """Offset that this response is for.""" data: bytes - len: int | None = None # only mandatory when “off” is 0. + """Data read from the file.""" + len: int | None = None + """The length of the file, only mandatory if “off” is 0.""" class FileUploadRequest(message.WriteRequest): + """Upload a file to a specified location. + + Command will automatically overwrite existing file or create a new one if it + does not exist at specified path. The protocol supports stateless upload + where each requests carries different chunk of a file and it is client side + responsibility to track progress of upload. + + Note that file handles will remain open for consecutive requests (as long as + an idle timeout has not been reached, but files are not exclusively owned by + SMP, for the time of download session, and may change between requests or + even be removed. Note that file handles will remain open for consecutive + requests (as long as an idle timeout has not been reached and another + transport does not make use of uploading/downloading files using fs_mgmt), + but files are not exclusively owned by MCUmgr, for the time of download + session, and may change between requests or even be removed. + """ + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_DOWNLOAD_UPLOAD off: int + """Offset to start/continue writing to.""" data: bytes + """Data to write to the file.""" name: str - len: int | None = None # this field is only mandatory when “off” is 0 + """Name of the file to upload.""" + len: int | None = None + """The length of the file, only mandatory if “off” is 0.""" class FileUploadResponse(message.WriteResponse): + """Success response to a file upload request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_DOWNLOAD_UPLOAD off: int + """Offset of the file.""" class FileStatusRequest(message.ReadRequest): + """Retrieve status of an existing file from specified path of a target device.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_STATUS @@ -52,6 +99,8 @@ class FileStatusRequest(message.ReadRequest): class FileStatusResponse(message.ReadResponse): + """Success response to a file status request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_STATUS @@ -59,115 +108,163 @@ class FileStatusResponse(message.ReadResponse): class FileHashChecksumRequest(message.ReadRequest): + """Generate a hash/checksum of an existing file at a specified path on a target device. + + Note that kernel heap memory is required for buffers to be allocated for + this to function, and large stack memory buffers are required for generation + of the output hash/checksum. Requires `CONFIG_MCUMGR_GRP_FS_CHECKSUM_HASH` to + be enabled for the base functionality, supported hash/checksum are opt-in + with `CONFIG_MCUMGR_GRP_FS_CHECKSUM_IEEE_CRC32` or + `CONFIG_MCUMGR_GRP_FS_HASH_SHA256`. + """ + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_HASH_CHECKSUM name: str - type: str | None = None # omit to use default. - off: int | None = None # 0 if not provided - len: int | None = None # full size if not provided + """Absolute path to the file to generate hash/checksum for.""" + type: Literal["crc32", "sha256"] | None = None + """Type of hash/checksum to generate, if not provided, default is used.""" + off: int | None = None + """Offset to start hash/checksum generation from, default 0.""" + len: int | None = None + """Maximum length of the file to generate hash/checksum for, default is entire file.""" class FileHashChecksumResponse(message.ReadResponse): + """Success response to a file hash/checksum request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_HASH_CHECKSUM - type: str - off: int | None = None # only present if not 0 + type: Literal["crc32", "sha256"] + off: int | None = None + """Only present if not 0.""" len: int + """Length of input data used to generate hash/checksum.""" output: int | bytes + """Output hash/checksum, depending on the type requested.""" class SupportedFileHashChecksumTypesRequest(message.ReadRequest): + """List the hash and checksum types are available on a device. + + Requires Kconfig `CONFIG_MCUMGR_GRP_FS_CHECKSUM_HASH_SUPPORTED_CMD` to be enabled. + """ + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.SUPPORTED_FILE_HASH_CHECKSUM_TYPES +class HashChecksumFormat(IntEnum): + """Format that the hash/checksum returns where 0 is for numerical and 1 is for byte array.""" + + NUMERICAL = 0 + BYTE_ARRAY = 1 + + class HashChecksumType(BaseModel): + """Hash and checksum type supported by the device.""" + model_config = ConfigDict(extra="forbid", frozen=True) - format: int + format: HashChecksumFormat + """Format that the hash/checksum returns where 0 is for numerical and 1 is for byte array.""" size: int + """Size of the hash/checksum in bytes.""" class SupportedFileHashChecksumTypesResponse(message.ReadResponse): + """Success response to a supported file hash/checksum types request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.SUPPORTED_FILE_HASH_CHECKSUM_TYPES - types: Dict[str, HashChecksumType] + types: Dict[Literal["crc32", "sha256"], HashChecksumType] + """The map of supported hash/checksum types.""" class FileCloseRequest(message.WriteRequest): + """Close all open file handles held by fs_mgmt upload/download requests.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_CLOSE class FileCloseResponse(message.WriteResponse): + """Success response to a file close request.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT _COMMAND_ID = header.CommandId.FileManagement.FILE_CLOSE @unique class FS_MGMT_ERR(IntEnum): + """File System Management error codes.""" + OK = 0 """No error (success).""" - UNKNOWN = auto() + UNKNOWN = 1 """ Unknown error. """ - FILE_INVALID_NAME = auto() + FILE_INVALID_NAME = 2 """The specified file name is not valid.""" - FILE_NOT_FOUND = auto() + FILE_NOT_FOUND = 3 """The specified file does not exist.""" - FILE_IS_DIRECTORY = auto() + FILE_IS_DIRECTORY = 4 """The specified file is a directory, not a file. """ - FILE_OPEN_FAILED = auto() + FILE_OPEN_FAILED = 5 """Error occurred whilst attempting to open a file.""" - FILE_SEEK_FAILED = auto() + FILE_SEEK_FAILED = 6 """Error occurred whilst attempting to seek to an offset in a file. """ - FILE_READ_FAILED = auto() + FILE_READ_FAILED = 7 """Error occurred whilst attempting to read data from a file.""" - FILE_TRUNCATE_FAILED = auto() + FILE_TRUNCATE_FAILED = 8 """Error occurred whilst trying to truncate file.""" - FILE_DELETE_FAILED = auto() + FILE_DELETE_FAILED = 9 """Error occurred whilst trying to delete file.""" - FILE_WRITE_FAILED = auto() + FILE_WRITE_FAILED = 10 """Error occurred whilst attempting to write data to a file.""" - FILE_OFFSET_NOT_VALID = auto() + FILE_OFFSET_NOT_VALID = 11 """The specified data offset is not valid, this could indicate that the file on the device has changed since the previous command. The length of the current file on the device is returned as "len", the user application needs to decide how to handle this (e.g. the hash of the file could be requested and compared with the hash of the length of the file being uploaded to see if they match or not).""" - FILE_OFFSET_LARGER_THAN_FILE = auto() + FILE_OFFSET_LARGER_THAN_FILE = 12 """The requested offset is larger than the size of the file on the device.""" - CHECKSUM_HASH_NOT_FOUND = auto() + CHECKSUM_HASH_NOT_FOUND = 13 """The requested checksum or hash type was not found or is not supported by this build.""" - MOUNT_POINT_NOT_FOUND = auto() + MOUNT_POINT_NOT_FOUND = 14 """The specified mount point was not found or is not mounted.""" - READ_ONLY_FILESYSTEM = auto() + READ_ONLY_FILESYSTEM = 15 """The specified mount point is that of a read-only filesystem.""" - FILE_EMPTY = auto() + FILE_EMPTY = 16 """The operation cannot be performed because the file is empty with no contents. """ class FileSystemManagementErrorV1(error.ErrorV1): + """File System Management error response.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT class FileSystemManagementErrorV2(error.ErrorV2[FS_MGMT_ERR]): + """File System Management error response.""" + _GROUP_ID = header.GroupId.FILE_MANAGEMENT diff --git a/smp/header.py b/smp/header.py index 1cf958d..b79101c 100644 --- a/smp/header.py +++ b/smp/header.py @@ -4,7 +4,7 @@ import struct from dataclasses import dataclass -from enum import IntEnum, IntFlag, auto, unique +from enum import IntEnum, IntFlag, unique from typing import ClassVar, Dict, Type, Union from typing_extensions import TypeAlias @@ -120,7 +120,7 @@ class _VERSION_BIT: @unique class Flag(IntFlag): - UNUSED = auto() + UNUSED = 0 @dataclass(frozen=True) diff --git a/smp/image_management.py b/smp/image_management.py index 7a117d4..82a7441 100644 --- a/smp/image_management.py +++ b/smp/image_management.py @@ -1,8 +1,30 @@ -"""The Simple Management Protocol (SMP) Image Management group.""" +"""The Simple Management Protocol (SMP) Image Management group. + +## Notion of "slots" and "images" in Zephyr + +The “slot” and “image” definition comes from mcuboot where “image” would consist +of two “slots”, further named “primary” and “secondary”; the application is +supposed to run from the “primary slot” and update is supposed to be uploaded to +the “secondary slot”; the mcuboot is responsible in swapping slots on boot. This +means that pair of slots is dedicated to single upgradable application. In case +of Zephyr this gets a little bit confusing because DTS will use +“slot0_partition” and “slot1_partition”, as label of fixed-partition dedicated +to single application, but will name them as “image-0” and “image-1” +respectively. + +Currently Zephyr supports at most two images, in which case mapping is as +follows: + +| Image | Slot labels | Slot Names | +|-------|-------------|------------| +| 0 | “slot0_partition” “slot1_partition” | “image-0” “image-1” | +| 1 | “slot2_partition” “slot3_partition” | “image-2” “image-3” | + +""" from __future__ import annotations -from enum import IntEnum, auto, unique +from enum import IntEnum, unique from typing import Generator, List from pydantic import BaseModel, ConfigDict, ValidationInfo, field_validator @@ -11,24 +33,68 @@ class HashBytes(bytes): # pragma: no cover - """Only to print something useful to the console.""" + """Only to print something useful to the `rich` console.""" def __rich_repr__(self) -> Generator[str, None, None]: yield self.hex().upper() class ImageState(BaseModel): + """The state of an image in a slot.""" + model_config = ConfigDict(extra="forbid", frozen=True, arbitrary_types_allowed=True) slot: int + """Slot number within “image”. + + Each image has two slots: + - primary (running one) = 0 + - secondary (for DFU dual-bank purposes) = 1. + """ version: str + """Version of the image.""" image: int | None = None + """Semi-optional image number. + + The field is not required when only one image is supported by the running + application. + """ hash: HashBytes | bytes | None = None + """SHA256 hash of the image header and body. + + Note that this will not be the same as the SHA256 of the whole file, it is + the field in the MCUboot TLV section that contains a hash of the data which + is used for signature verification purposes. This field is optional but only + optional when using MCUboot's serial recovery feature with one pair of image + slots, `CONFIG_BOOT_SERIAL_IMG_GRP_HASH` can be disabled to remove + support for hashes in this configuration. SMP server in applications must + support sending hashes. + """ bootable: bool | None = None + """True if image has bootable flag set. + + This field does not have to be present if false. + """ pending: bool | None = None + """True if image is set for next swap. + + This field does not have to be present if false. + """ confirmed: bool | None = None + """True if image has been confirmed. + + This field does not have to be present if false. + """ active: bool | None = None + """True if image is currently active application + + This field does not have to be present if false. + """ permanent: bool | None = None + """True if image is to stay in primary slot after the next boot. + + This does not have to be present if false. + """ @field_validator("hash") @classmethod @@ -39,43 +105,107 @@ def cast_bytes(cls, v: bytes | None, _: ValidationInfo) -> HashBytes | None: class ImageStatesReadRequest(message.ReadRequest): + """Obtain list of images with their current state.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.STATE class ImageStatesReadResponse(message.ReadResponse): + """Response to an image state request.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.STATE images: List[ImageState] + """List of images with their current state.""" splitStatus: int | None = None + """States whether loader of split image is compatible with application part. + + This is unused by Zephyr. + """ class ImageStatesWriteRequest(message.WriteRequest): + """Set the state of an image. + + If “confirm” is false or not provided, an image with the “hash” will be set + for test, which means that it will not be marked as permanent and upon hard + reset the previous application will be restored to the primary slot. In case + when “confirm” is true, the “hash” is optional as the currently running + application will be assumed as target for confirmation. + """ + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.STATE - hash: bytes + hash: bytes | None = None + """SHA256 hash of the image header and body.""" confirm: bool = False + """Confirm the image given by hash. + + CAUTION: it is dangerous to confirm the image before it has been booted! + + Setting this to true will mark the image as confirmed before it has been + booted, which can brick the device. Zephyr provides hooks for confirming + the image from within the application, so this is not necessary for normal + operation. + """ class ImageStatesWriteResponse(ImageStatesReadResponse): - pass + """Success response to an image state write request.""" class ImageUploadWriteRequest(message.WriteRequest): + """Upload an image to the device. + + The image is uploaded in chunks, with each chunk being sent in a separate + request. The first request must include the image's length and image number. + """ + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.UPLOAD off: int + """The offset of the data chunk in this request.""" data: bytes - image: int | None = None # 'required' when off == 0 - len: int | None = None # required when off == 0 - sha: bytes | None = None # should be sent when off == 0 - upgrade: bool | None = None # allowed when off == 0 + """The data chunk to write.""" + image: int | None = None + """Image number. + + It does not have to appear in request at all, in which case it is assumed to + be 0. Should only be present when `off` is 0. + """ + len: int | None = None + """The length of the image. + + Required when `off` is 0, otherwise ignored. + """ + sha: bytes | None = None + """SHA256 hash of the image. + + This is used to identify an upload session (e.g. to allow to continue + a broken session), and for image verification purposes. This must be a full + SHA256 hash of the whole image being uploaded, or not included if the hash + is not available (in which case, upload session continuation and image + verification functionality will be unavailable). Should only be present when + “off” is 0. + """ + upgrade: bool | None = None + """Optional flag that states that only upgrade should be allowed. + + If the version of uploaded software is not higher then already on a device, + the image upload will be rejected. Zephyr compares major, minor and + revision (x.y.z) by default unless `CONFIG_MCUMGR_GRP_IMG_VERSION_CMP_USE_BUILD_NUMBER` + is set, whereby it will compare build numbers too. Should only be present + when “off” is 0. + """ class ImageUploadWriteResponse(message.WriteResponse): + """Success response to an image upload request.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.UPLOAD @@ -111,6 +241,8 @@ class ImageUploadWriteResponse(message.WriteResponse): class ImageEraseRequest(message.WriteRequest): + """Erase an image from a slot.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.ERASE @@ -119,118 +251,126 @@ class ImageEraseRequest(message.WriteRequest): class ImageEraseResponse(message.WriteResponse): + """Success response to an image erase request.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT _COMMAND_ID = header.CommandId.ImageManagement.ERASE @unique class IMG_MGMT_ERR(IntEnum): + """Image management error codes.""" + OK = 0 """No error, this is implied if there is no ret value in the response""" - UNKNOWN = auto() + UNKNOWN = 1 """Unknown error occurred.""" - FLASH_CONFIG_QUERY_FAIL = auto() + FLASH_CONFIG_QUERY_FAIL = 2 """Failed to query flash area configuration.""" - NO_IMAGE = auto() + NO_IMAGE = 3 """There is no image in the slot.""" - NO_TLVS = auto() + NO_TLVS = 4 """The image in the slot has no TLVs (tag, length, value).""" - INVALID_TLV = auto() + INVALID_TLV = 5 """The image in the slot has an invalid TLV type and/or length.""" - TLV_MULTIPLE_HASHES_FOUND = auto() + TLV_MULTIPLE_HASHES_FOUND = 6 """The image in the slot has multiple hash TLVs, which is invalid.""" - TLV_INVALID_SIZE = auto() + TLV_INVALID_SIZE = 7 """The image in the slot has an invalid TLV size.""" - HASH_NOT_FOUND = auto() + HASH_NOT_FOUND = 8 """The image in the slot does not have a hash TLV, which is required.""" - NO_FREE_SLOT = auto() + NO_FREE_SLOT = 9 """There is no free slot to place the image.""" - FLASH_OPEN_FAILED = auto() + FLASH_OPEN_FAILED = 10 """Flash area opening failed.""" - FLASH_READ_FAILED = auto() + FLASH_READ_FAILED = 11 """Flash area reading failed.""" - FLASH_WRITE_FAILED = auto() + FLASH_WRITE_FAILED = 12 """Flash area writing failed.""" - FLASH_ERASE_FAILED = auto() + FLASH_ERASE_FAILED = 13 """Flash area erase failed.""" - INVALID_SLOT = auto() + INVALID_SLOT = 14 """The provided slot is not valid.""" - NO_FREE_MEMORY = auto() + NO_FREE_MEMORY = 15 """Insufficient heap memory (malloc failed).""" - FLASH_CONTEXT_ALREADY_SET = auto() + FLASH_CONTEXT_ALREADY_SET = 16 """The flash context is already set.""" - FLASH_CONTEXT_NOT_SET = auto() + FLASH_CONTEXT_NOT_SET = 17 """The flash context is not set.""" - FLASH_AREA_DEVICE_NULL = auto() + FLASH_AREA_DEVICE_NULL = 18 """The device for the flash area is NULL.""" - INVALID_PAGE_OFFSET = auto() + INVALID_PAGE_OFFSET = 19 """The offset for a page number is invalid.""" - INVALID_OFFSET = auto() + INVALID_OFFSET = 20 """The offset parameter was not provided and is required.""" - INVALID_LENGTH = auto() + INVALID_LENGTH = 21 """The length parameter was not provided and is required.""" - INVALID_IMAGE_HEADER = auto() + INVALID_IMAGE_HEADER = 22 """The image length is smaller than the size of an image header.""" - INVALID_IMAGE_HEADER_MAGIC = auto() + INVALID_IMAGE_HEADER_MAGIC = 23 """The image header magic value does not match the expected value.""" - INVALID_HASH = auto() + INVALID_HASH = 24 """The hash parameter provided is not valid.""" - INVALID_FLASH_ADDRESS = auto() + INVALID_FLASH_ADDRESS = 25 """The image load address does not match the address of the flash area.""" - VERSION_GET_FAILED = auto() + VERSION_GET_FAILED = 26 """Failed to get version of currently running application.""" - CURRENT_VERSION_IS_NEWER = auto() + CURRENT_VERSION_IS_NEWER = 27 """The currently running application is newer than the version being uploaded.""" - IMAGE_ALREADY_PENDING = auto() + IMAGE_ALREADY_PENDING = 28 """There is already an image operating pending.""" - INVALID_IMAGE_VECTOR_TABLE = auto() + INVALID_IMAGE_VECTOR_TABLE = 29 """The image vector table is invalid.""" - INVALID_IMAGE_TOO_LARGE = auto() + INVALID_IMAGE_TOO_LARGE = 30 """The image it too large to fit.""" - INVALID_IMAGE_DATA_OVERRUN = auto() + INVALID_IMAGE_DATA_OVERRUN = 31 """The amount of data sent is larger than the provided image size.""" - IMAGE_CONFIRMATION_DENIED = auto() + IMAGE_CONFIRMATION_DENIED = 32 """Confirmation of image has been denied""" - IMAGE_SETTING_TEST_TO_ACTIVE_DENIED = auto() + IMAGE_SETTING_TEST_TO_ACTIVE_DENIED = 33 """Setting test to active slot is not allowed""" class ImageManagementErrorV1(error.ErrorV1): + """Image Management error response.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT class ImageManagementErrorV2(error.ErrorV2[IMG_MGMT_ERR]): + """Image Management error response.""" + _GROUP_ID = header.GroupId.IMAGE_MANAGEMENT diff --git a/smp/message.py b/smp/message.py index a1b51ea..37c9163 100644 --- a/smp/message.py +++ b/smp/message.py @@ -22,6 +22,8 @@ class _MessageBase(ABC, BaseModel): + """The base class for SMP messages.""" + model_config = ConfigDict(extra="forbid", frozen=True) _OP: ClassVar[smpheader.OP] @@ -167,7 +169,7 @@ def model_post_init(self, _: None) -> None: class Request(_MessageBase, ABC): - ... + """Base class for SMP Requests.""" @unique @@ -180,6 +182,8 @@ class ResponseType(IntEnum): class Response(_MessageBase, ABC): + """Base class for SMP Responses.""" + RESPONSE_TYPE: ClassVar[ResponseType] diff --git a/smp/os_management.py b/smp/os_management.py index 6cd7a8f..5eb17f3 100644 --- a/smp/os_management.py +++ b/smp/os_management.py @@ -2,8 +2,8 @@ from __future__ import annotations -from enum import IntEnum, auto, unique -from typing import Any, Dict +from enum import IntEnum, unique +from typing import Any, Dict, Literal from pydantic import BaseModel, ConfigDict, Field @@ -11,35 +11,68 @@ class EchoWriteRequest(message.WriteRequest): + """Echo back the provided string.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.ECHO d: str + """String to echo.""" class EchoWriteResponse(message.WriteResponse): + """Success response to an echo request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.ECHO r: str + """Echoed string.""" class ResetWriteRequest(message.WriteRequest): + """Performs reset of system. + + The device should issue response before resetting so that the SMP client + could receive information that the command has been accepted. By default, + this command is accepted in all conditions, however if the + `CONFIG_MCUMGR_GRP_OS_RESET_HOOK` is enabled and an application registers a + callback, the callback will be called when this command is issued and can be + used to perform any necessary tidy operations prior to the module rebooting, + or to reject the reset request outright altogether with an error response. + + For details on this functionality, see [callbacks](https://docs.zephyrproject.org/latest/services/device_mgmt/mcumgr_callbacks.html#mcumgr-callbacks). # noqa: E501 + """ + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.RESET + force: Literal[0, 1] | None = None + """Force reset. + + Normally the command sends an empty CBOR map as data, but if a previous + reset attempt has responded with “rc” equal to MGMT_ERR_EBUSY then the + following map may be sent to force a reset + """ + class ResetWriteResponse(message.WriteResponse): + """Success response to a reset request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.RESET class TaskStatisticsReadRequest(message.ReadRequest): + """Request task statistics.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.TASK_STATS class TaskStatistics(BaseModel): + """Task statistics.""" + model_config = ConfigDict(extra="forbid", frozen=True) prio: int @@ -69,18 +102,25 @@ class TaskStatistics(BaseModel): class TaskStatisticsReadResponse(message.ReadResponse): + """Task statistics response.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.TASK_STATS tasks: Dict[str, TaskStatistics] + """Task statistics map.""" class MemoryPoolStatisticsReadRequest(message.ReadRequest): + """Request memory pool statistics.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.MEMORY_POOL_STATS class MemoryPoolStatistics(BaseModel): + """Memory pool statistics.""" + model_config = ConfigDict(extra="forbid", frozen=True) blksize: int @@ -103,11 +143,15 @@ class MemoryPoolStatisticsReadResponse(message.ReadResponse): class DateTimeReadRequest(message.ReadRequest): + """Request the current date and time.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.DATETIME_STRING class DateTimeReadResponse(message.ReadResponse): + """Response to a date and time request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.DATETIME_STRING @@ -115,6 +159,8 @@ class DateTimeReadResponse(message.ReadResponse): class DateTimeWriteRequest(message.WriteRequest): + """Set the current date and time.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.DATETIME_STRING @@ -122,24 +168,34 @@ class DateTimeWriteRequest(message.WriteRequest): class DateTimeWriteResponse(message.WriteResponse): + """Success response to a date and time request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.DATETIME_STRING class MCUMgrParametersReadRequest(message.ReadRequest): + """Request MCU Manager parameters.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.MCUMGR_PARAMETERS class MCUMgrParametersReadResponse(message.ReadResponse): + """Success response to a MCU Manager parameters request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.MCUMGR_PARAMETERS buf_size: int + """Single SMP buffer size, this includes SMP header and CBOR payload.""" buf_count: int + """Number of SMP buffers.""" class OSApplicationInfoReadRequest(message.ReadRequest): + """Request information about the application running on the device.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.OS_APPLICATION_INFO @@ -166,13 +222,18 @@ class OSApplicationInfoReadRequest(message.ReadRequest): class OSApplicationInfoReadResponse(message.ReadResponse): + """Success response to an application information request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.OS_APPLICATION_INFO output: str + """Text response including requested parameters.""" class BootloaderInformationReadRequest(message.ReadRequest): + """Request bootloader information.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.BOOTLOADER_INFO @@ -201,6 +262,8 @@ class MCUbootMode(IntEnum): class MCUbootModeQueryResponse(BaseModel): + """Response to a MCUboot mode query.""" + model_config = ConfigDict(extra="forbid", frozen=True) mode: MCUbootMode @@ -208,6 +271,8 @@ class MCUbootModeQueryResponse(BaseModel): class BootloaderInformationReadResponse(message.ReadResponse): + """Success response to a bootloader information request.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT _COMMAND_ID = header.CommandId.OSManagement.BOOTLOADER_INFO @@ -224,28 +289,34 @@ class BootloaderInformationReadResponse(message.ReadResponse): @unique class OS_MGMT_RET_RC(IntEnum): + """OS Management return codes.""" + OK = 0 """No error, this is implied if there is no ret value in the response.""" - UNKNOWN = auto() + UNKNOWN = 1 """Unknown error occurred.""" - INVALID_FORMAT = auto() + INVALID_FORMAT = 2 """The provided format value is not valid.""" - QUERY_YIELDS_NO_ANSWER = auto() + QUERY_YIELDS_NO_ANSWER = 3 """Query was not recognized.""" - RTC_NOT_SET = auto() + RTC_NOT_SET = 4 """RTC is not set.""" - RTC_COMMAND_FAILED = auto() + RTC_COMMAND_FAILED = 5 """RTC command failed.""" class OSManagementErrorV1(error.ErrorV1): + """OS Management error response.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT class OSManagementErrorV2(error.ErrorV2[OS_MGMT_RET_RC]): + """OS Management error response.""" + _GROUP_ID = header.GroupId.OS_MANAGEMENT diff --git a/smp/packet.py b/smp/packet.py index a00125f..83d6958 100644 --- a/smp/packet.py +++ b/smp/packet.py @@ -28,7 +28,52 @@ def encode(request: bytes, line_length: int = 8192) -> Generator[bytes, None, None]: - """Iteratively pack an SMP bytes to packets of `line_length` size.""" + """Iteratively pack an SMP bytes to packets of `line_length` size. + + Note: only the USB/serial transport uses this encoding and fragmentation. + The BLE and UDP transports can simply send the SMP frames as they are. + + Params: + - request: The bytes to be encoded. + - line_length: The maximum length of each packet. + + Example: + + Encode arbitrary bytes to SMP packets: + + ```python + from smp.packet import encode + + for packet in encode(b"Hello, world!"): + print(packet) + ``` + prints: + ``` + b'\\x06\\tAA9IZWxsbywgd29ybGQhet4=\\n' + ``` + + Set a smaller packet size to fragment: + + ```python + from smp.packet import encode + + for packet in encode(b"Hello, world!", line_length=8): + print(packet) + ``` + prints: + ``` + b'\\x06\\tAA9I\\n' + b'\\x04\\x14ZWxs\\n' + b'\\x04\\x14bywg\\n' + b'\\x04\\x14d29y\\n' + b'\\x04\\x14bGQh\\n' + b'\\x04\\x14et4=\\n' + ``` + + The packet size should be set according to the buffer sizes of the SMP + server, and larger buffers will be more efficient. It is not necessary to + use multiple buffers. + """ logger.debug(f"Serializing {request=}") @@ -55,7 +100,63 @@ def encode(request: bytes, line_length: int = 8192) -> Generator[bytes, None, No def decode() -> Generator[None, bytes, bytes]: - """Iteratively unpack a series of SMP packets to bytes.""" + """Iteratively unpack a series of SMP packets to SMP frame bytes. + + Example: + + ```python + from smp.packet import decode + from smp.image_management import ImageUploadWriteRequest + + # Example encoded SMP packets + packets = ( + bytes.fromhex("06094148344b41414230414147610a"), + bytes.fromhex("041441615a6a6247567544574e760a"), + bytes.fromhex("04145a6d59594b6d4e7a6147464d0a"), + bytes.fromhex("041464484a3163335167625755670a"), + bytes.fromhex("0414596e4a765a475268644746590a"), + bytes.fromhex("04145141414241674d45425159480a"), + bytes.fromhex("041443416b4b4377774e446738510a"), + bytes.fromhex("041445524954464255574678675a0a"), + bytes.fromhex("04144768736348523466494345690a"), + bytes.fromhex("04144979516c4a69636f4b536f720a"), + bytes.fromhex("04144c4330754c7a41784d6a4d300a"), + bytes.fromhex("04144e5459334f446b364f7a77390a"), + bytes.fromhex("0414506a396c615731685a3255410a"), + bytes.fromhex("04145a3356775a334a685a4758300a"), + bytes.fromhex("04147134673d0a"), + ) + + decoder = decode() + next(decoder) + + for packet in packets: + try: + decoder.send(packet) + except StopIteration as e: + frame = e.value + break + + print(frame) + print(ImageUploadWriteRequest.loads(frame)) + ``` + prints: + ``` + bytearray(b'\\n\\x00\\x00t\\x00\\x01\\x9a\\x01\\xa6clen\\rcoff\\x18*cshaLtrust me broddataX@\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !"#$%&\\'()*+,-./0123456789:;<=>?eimage\\x00gupgrade\\xf4') # noqa: E501 + ``` + and + ``` + header=Header(op=, version=, flags=, + length=116, group_id=1, sequence=154, command_id=1) version= + sequence=154 smp_data=b'\\n\\x00\\x00t\\x00\\x01\\x9a\\x01\\xa6clen\\rcoff\\x18*cshaL + trust me broddataX@\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e + \\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !"#$%& + \\'()*+,-./0123456789:;<=>?eimage\\x00gupgrade\\xf4' off=42 data=b'\\x00\\x01\\x02 + \\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16 + \\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !"#$%&\\'()*+,-./0123456789:;<=>?' + image=0 len=13 sha=b'trust me bro' upgrade=False + ``` + """ packet = yield diff --git a/smp/settings_management.py b/smp/settings_management.py index b36d3eb..323f937 100644 --- a/smp/settings_management.py +++ b/smp/settings_management.py @@ -129,6 +129,8 @@ class SaveSettingsResponse(smpmsg.WriteResponse): @unique class SETTINGS_MGMT_ERR(IntEnum): + """Return codes for the settings management group.""" + OK = 0 """No error, this is implied if there is no ret value in the response.""" @@ -155,8 +157,12 @@ class SETTINGS_MGMT_ERR(IntEnum): class SettingsManagementErrorV1(smperr.ErrorV1): + """Error response to a settings management command.""" + _GROUP_ID = smphdr.GroupId.SETTINGS_MANAGEMENT class SettingsManagementErrorV2(smperr.ErrorV2[SETTINGS_MGMT_ERR]): + """Error response to a settings management command.""" + _GROUP_ID = smphdr.GroupId.SETTINGS_MANAGEMENT diff --git a/smp/shell_management.py b/smp/shell_management.py index 68d06fd..ed351cd 100644 --- a/smp/shell_management.py +++ b/smp/shell_management.py @@ -1,13 +1,15 @@ """The Simple Management Protocol (SMP) Shell Management group.""" -from enum import IntEnum, auto, unique +from enum import IntEnum, unique from typing import List from smp import error, header, message class ExecuteRequest(message.WriteRequest): + """Execute a shell command.""" + _GROUP_ID = header.GroupId.SHELL_MANAGEMENT _COMMAND_ID = header.CommandId.ShellManagement.EXECUTE @@ -15,6 +17,8 @@ class ExecuteRequest(message.WriteRequest): class ExecuteResponse(message.WriteResponse): + """Success response to a shell command execution.""" + _GROUP_ID = header.GroupId.SHELL_MANAGEMENT _COMMAND_ID = header.CommandId.ShellManagement.EXECUTE @@ -24,19 +28,25 @@ class ExecuteResponse(message.WriteResponse): @unique class SHELL_MGMT_RET_RC(IntEnum): + """Return codes for the shell management group.""" + OK = 0 """No error, this is implied if there is no ret value in the response.""" - UNKNOWN = auto() + UNKNOWN = 1 """Unknown error occurred.""" - INVALID_FORMAT = auto() + INVALID_FORMAT = 2 """The provided format value is not valid.""" class ShellManagementErrorV1(error.ErrorV1): + """Error response to a shell command execution.""" + _GROUP_ID = header.GroupId.SHELL_MANAGEMENT class ShellManagementErrorV2(error.ErrorV2[SHELL_MGMT_RET_RC]): + """Error response to a shell command execution.""" + _GROUP_ID = header.GroupId.SHELL_MANAGEMENT diff --git a/smp/statistics_management.py b/smp/statistics_management.py index aedc571..656676a 100644 --- a/smp/statistics_management.py +++ b/smp/statistics_management.py @@ -10,6 +10,8 @@ class GroupDataRequest(smpmsg.ReadRequest): + """Read the statistics group data.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT _COMMAND_ID = smphdr.CommandId.StatisticsManagement.GROUP_DATA @@ -17,6 +19,8 @@ class GroupDataRequest(smpmsg.ReadRequest): class GroupDataResponse(smpmsg.ReadResponse): + """Statistics group data response.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT _COMMAND_ID = smphdr.CommandId.StatisticsManagement.GROUP_DATA @@ -25,11 +29,15 @@ class GroupDataResponse(smpmsg.ReadResponse): class ListOfGroupsRequest(smpmsg.ReadRequest): + """List the available statistics groups.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT _COMMAND_ID = smphdr.CommandId.StatisticsManagement.LIST_OF_GROUPS class ListOfGroupsResponse(smpmsg.ReadResponse): + """List of available statistics groups.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT _COMMAND_ID = smphdr.CommandId.StatisticsManagement.LIST_OF_GROUPS @@ -38,6 +46,8 @@ class ListOfGroupsResponse(smpmsg.ReadResponse): @unique class STAT_MGMT_ERR(IntEnum): + """Return codes for the statistics management group.""" + OK = 0 """No error, this is implied if there is no ret value in the response.""" @@ -58,8 +68,12 @@ class STAT_MGMT_ERR(IntEnum): class StatisticsManagementErrorV1(smperr.ErrorV1): + """Error response to a statistics management command.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT class StatisticsManagementErrorV2(smperr.ErrorV2[STAT_MGMT_ERR]): + """Error response to a statistics management command.""" + _GROUP_ID = smphdr.GroupId.STATISTICS_MANAGEMENT diff --git a/smp/user/__init__.py b/smp/user/__init__.py index e69de29..cb594f0 100644 --- a/smp/user/__init__.py +++ b/smp/user/__init__.py @@ -0,0 +1 @@ +"""Users may define their own management groups starting at GroupId 64.""" diff --git a/smp/user/intercreate.py b/smp/user/intercreate.py index 312df08..e2caa12 100644 --- a/smp/user/intercreate.py +++ b/smp/user/intercreate.py @@ -2,12 +2,14 @@ from __future__ import annotations -from enum import IntEnum, auto, unique +from enum import IntEnum, unique from smp import error, header, message class ImageUploadWriteRequest(message.WriteRequest): + """Upload an image to an application-defined location like a secondary MCU.""" + _GROUP_ID = header.UserGroupId.INTERCREATE _COMMAND_ID = header.CommandId.Intercreate.UPLOAD @@ -28,6 +30,8 @@ class ImageUploadWriteRequest(message.WriteRequest): class ImageUploadWriteResponse(message.WriteResponse): + """Success response to an image upload request.""" + _GROUP_ID = header.UserGroupId.INTERCREATE _COMMAND_ID = header.CommandId.Intercreate.UPLOAD @@ -37,16 +41,22 @@ class ImageUploadWriteResponse(message.WriteResponse): @unique class IC_MGMT_ERR(IntEnum): + """Intercreate Management error codes.""" + OK = 0 """No error.""" - INVALID_IMAGE = auto() + INVALID_IMAGE = 1 """No image matched the image provided.""" class ErrorV1(error.ErrorV1): + """Intercreate Management error response.""" + _GROUP_ID = header.UserGroupId.INTERCREATE class ErrorV2(error.ErrorV2[IC_MGMT_ERR]): + """Intercreate Management error response.""" + _GROUP_ID = header.UserGroupId.INTERCREATE diff --git a/smp/zephyr_management.py b/smp/zephyr_management.py index 8aa0b42..c074405 100644 --- a/smp/zephyr_management.py +++ b/smp/zephyr_management.py @@ -9,17 +9,23 @@ class EraseStorageRequest(smpmsg.WriteRequest): + """Erase the storage area.""" + _GROUP_ID = smphdr.GroupId.ZEPHYR_MANAGEMENT _COMMAND_ID = smphdr.CommandId.ZephyrManagement.ERASE_STORAGE class EraseStorageResponse(smpmsg.WriteResponse): + """Success response to a storage area erase.""" + _GROUP_ID = smphdr.GroupId.ZEPHYR_MANAGEMENT _COMMAND_ID = smphdr.CommandId.ZephyrManagement.ERASE_STORAGE @unique class ZEPHYRBASIC_MGMT_ERR(IntEnum): + """Return codes for the Zephyr Management group.""" + OK = 0 """No error, this is implied if there is no ret value in the response""" @@ -37,8 +43,12 @@ class ZEPHYRBASIC_MGMT_ERR(IntEnum): class ZephyrManagementErrorV1(smperr.ErrorV1): + """Error response to a Zephyr Management command.""" + _GROUP_ID = smphdr.GroupId.ZEPHYR_MANAGEMENT class ZephyrManagementErrorV2(smperr.ErrorV2[ZEPHYRBASIC_MGMT_ERR]): + """Error response to a Zephyr Management command.""" + _GROUP_ID = smphdr.GroupId.ZEPHYR_MANAGEMENT