diff --git a/.bandit.yml b/.bandit.yml deleted file mode 100644 index 781fb8f..0000000 --- a/.bandit.yml +++ /dev/null @@ -1,6 +0,0 @@ ---- -skips: [] -# No need to check for security issues in the test scripts! -exclude_dirs: - - "./tests/" - - "./jdiff/tests/" diff --git a/.flake8 b/.flake8 deleted file mode 100644 index e3ba27d..0000000 --- a/.flake8 +++ /dev/null @@ -1,4 +0,0 @@ -[flake8] -# E501: Line length is enforced by Black, so flake8 doesn't need to check it -# W503: Black disagrees with this rule, as does PEP 8; Black wins -ignore = E501, W503 diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 82a7c5d..3685cdf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ # Default owner(s) of all files in this repository -* @chadell @lvrfrc87 @scetron @pke11y @grelleum @jeffkala @pszulczewski +* @lvrfrc87 @scetron @grelleum @jeffkala @pszulczewski diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9e1e50f..4b69dfe 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -4,7 +4,7 @@ about: Report a reproducible bug in the current release of jdiff --- ### Environment -* Python version: +* Python version: * jdiff version: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ccec4f3..2b7addb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,9 @@ --- name: "CI" -on: # yamllint disable-line rule:truthy rule:comments +concurrency: # Cancel any existing runs of this workflow for this same PR + group: "${{ github.workflow }}-${{ github.ref }}" + cancel-in-progress: true +on: # yamllint disable-line rule:truthy rule:comments push: branches: - "main" @@ -9,24 +12,15 @@ on: # yamllint disable-line rule:truthy rule:comments - "v*" pull_request: ~ +env: + INVOKE_JDIFF_IMAGE_NAME: "jdiff" + INVOKE_JDIFF_IMAGE_VER: "latest" + jobs: - black: - runs-on: "ubuntu-24.04" - env: - INVOKE_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v4" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v6" - with: - poetry-version: "1.8.5" - - name: "Linting: black" - run: "poetry run invoke black" - mypy: + ruff-format: runs-on: "ubuntu-24.04" env: - INVOKE_LOCAL: "True" + INVOKE_JDIFF_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -34,12 +28,12 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" with: poetry-version: "1.8.5" - - name: "Linting: mypy" - run: "poetry run invoke mypy" - bandit: + - name: "Linting: ruff format" + run: "poetry run invoke ruff --action format" + ruff-lint: runs-on: "ubuntu-24.04" env: - INVOKE_LOCAL: "True" + INVOKE_JDIFF_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -47,12 +41,26 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" with: poetry-version: "1.8.5" - - name: "Linting: bandit" - run: "poetry run invoke bandit" - pydocstyle: + - name: "Linting: ruff" + run: "poetry run invoke ruff --action lint" + # Temporarily disabled due to issues with the docs build and needing best practices for NTC python builds. + # check-docs-build: + # runs-on: "ubuntu-24.04" + # env: + # INVOKE_JDIFF_LOCAL: "True" + # steps: + # - name: "Check out repository code" + # uses: "actions/checkout@v4" + # - name: "Setup environment" + # uses: "networktocode/gh-action-setup-poetry-environment@v6" + # with: + # poetry-version: "1.8.5" + # - name: "Check Docs Build" + # run: "poetry run invoke build-and-check-docs" + poetry: runs-on: "ubuntu-24.04" env: - INVOKE_LOCAL: "True" + INVOKE_JDIFF_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -60,25 +68,16 @@ jobs: uses: "networktocode/gh-action-setup-poetry-environment@v6" with: poetry-version: "1.8.5" - - name: "Linting: pydocstyle" - run: "poetry run invoke pydocstyle" - flake8: - runs-on: "ubuntu-24.04" - env: - INVOKE_LOCAL: "True" - steps: - - name: "Check out repository code" - uses: "actions/checkout@v4" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v6" - with: - poetry-version: "1.8.5" - - name: "Linting: flake8" - run: "poetry run invoke flake8" + - name: "Checking: poetry lock file" + run: "poetry lock --check" + needs: + - "ruff-format" + - "ruff-lint" + - "yamllint" yamllint: runs-on: "ubuntu-24.04" env: - INVOKE_LOCAL: "True" + INVOKE_JDIFF_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -88,61 +87,106 @@ jobs: poetry-version: "1.8.5" - name: "Linting: yamllint" run: "poetry run invoke yamllint" - pylint: needs: - - "bandit" - - "pydocstyle" - - "flake8" - - "yamllint" - - "black" - - "mypy" + - "ruff-format" + - "ruff-lint" + pylint: runs-on: "ubuntu-24.04" strategy: fail-fast: true matrix: - python-version: ["3.8"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] env: PYTHON_VER: "${{ matrix.python-version }}" - INVOKE_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" - name: "Setup environment" uses: "networktocode/gh-action-setup-poetry-environment@v6" + - name: "Get image version" + run: "echo INVOKE_JDIFF_IMAGE_VER=`poetry version -s`-py${{ matrix.python-version }} >> $GITHUB_ENV" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2" # v3.10.0 + - name: "Build" + uses: "docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25" # v5.4.0 with: - poetry-version: "1.8.5" - # Default install options is "--only dev", but we need to install all dependencies - poetry-install-options: "" + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + load: true + tags: "${{ env.INVOKE_JDIFF_IMAGE_NAME }}:${{ env.INVOKE_JDIFF_IMAGE_VER }}" + file: "./Dockerfile" + cache-from: "type=gha,scope=${{ env.INVOKE_JDIFF_IMAGE_NAME }}-${{ env.INVOKE_JDIFF_IMAGE_VER }}-py${{ matrix.python-version }}" + cache-to: "type=gha,scope=${{ env.INVOKE_JDIFF_IMAGE_NAME }}-${{ env.INVOKE_JDIFF_IMAGE_VER }}-py${{ matrix.python-version }}" + build-args: | + PYTHON_VER=${{ env.PYTHON_VER }} + - name: "Debug: Show docker images" + run: "docker image ls" - name: "Linting: Pylint" run: "poetry run invoke pylint" - pytest: needs: - - "pylint" + - "poetry" + pytest: strategy: fail-fast: true matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] runs-on: "ubuntu-24.04" env: PYTHON_VER: "${{ matrix.python-version }}" - INVOKE_LOCAL: "True" steps: - name: "Check out repository code" uses: "actions/checkout@v4" - name: "Setup environment" uses: "networktocode/gh-action-setup-poetry-environment@v6" + - name: "Get image version" + run: "echo INVOKE_JDIFF_IMAGE_VER=`poetry version -s`-py${{ matrix.python-version }} >> $GITHUB_ENV" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2" # v3.10.0 + - name: "Build" + uses: "docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25" # v5.4.0 with: - poetry-version: "1.8.5" - # Default install options is "--only dev", but we need to install all dependencies - poetry-install-options: "" + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + load: true + tags: "${{ env.INVOKE_JDIFF_IMAGE_NAME }}:${{ env.INVOKE_JDIFF_IMAGE_VER }}" + file: "./Dockerfile" + cache-from: "type=gha,scope=${{ env.INVOKE_JDIFF_IMAGE_NAME }}-${{ env.INVOKE_JDIFF_IMAGE_VER }}-py${{ matrix.python-version }}" + cache-to: "type=gha,scope=${{ env.INVOKE_JDIFF_IMAGE_NAME }}-${{ env.INVOKE_JDIFF_IMAGE_VER }}-py${{ matrix.python-version }}" + build-args: | + PYTHON_VER=${{ env.PYTHON_VER }} + - name: "Debug: Show docker images" + run: "docker image ls" - name: "Run Tests" run: "poetry run invoke pytest" - publish_gh: needs: - - "pytest" + - "poetry" + changelog: + if: > + contains(fromJson('["develop"]'), github.base_ref) && + (github.head_ref != 'main') && (!startsWith(github.head_ref, 'release')) + runs-on: "ubuntu-22.04" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v4" + with: + fetch-depth: "0" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v6" + with: + poetry-version: "1.8.5" + - name: "Check for changelog entry" + run: | + git fetch --no-tags origin +refs/heads/${{ github.base_ref }}:refs/remotes/origin/${{ github.base_ref }} + poetry run towncrier check --compare-with origin/${{ github.base_ref }} + publish_gh: name: "Publish to GitHub" runs-on: "ubuntu-24.04" - if: "startsWith(github.ref, 'refs/tags/v')" + # yamllint disable-line rule:quoted-strings + if: startsWith(github.ref, 'refs/tags/v') steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -156,22 +200,21 @@ jobs: run: "echo RELEASE_VERSION=${GITHUB_REF:10} >> $GITHUB_ENV" - name: "Run Poetry Version" run: "poetry version $RELEASE_VERSION" + - name: "Build Documentation" + run: "poetry run invoke build-and-check-docs" - name: "Run Poetry Build" run: "poetry build" - name: "Upload binaries to release" - uses: "svenstaro/upload-release-action@v2" - with: - repo_token: "${{ secrets.NTC_GITHUB_TOKEN }}" - file: "dist/*" - tag: "${{ github.ref }}" - overwrite: true - file_glob: true - publish_pypi: + run: "gh release upload ${{ github.ref_name }} dist/*.{tar.gz,whl}" + env: + GH_TOKEN: "${{ secrets.NTC_GITHUB_TOKEN }}" needs: - "pytest" + publish_pypi: name: "Push Package to PyPI" runs-on: "ubuntu-24.04" - if: "startsWith(github.ref, 'refs/tags/v')" + # yamllint disable-line rule:quoted-strings + if: startsWith(github.ref, 'refs/tags/v') steps: - name: "Check out repository code" uses: "actions/checkout@v4" @@ -188,15 +231,16 @@ jobs: - name: "Run Poetry Build" run: "poetry build" - name: "Push to PyPI" - uses: "pypa/gh-action-pypi-publish@release/v1" + uses: "pypa/gh-action-pypi-publish@76f52bc884231f62b9a034ebfe128415bbaabdfc" # v1.12.4 with: user: "__token__" password: "${{ secrets.PYPI_API_TOKEN }}" + needs: + - "pytest" slack-notify: needs: - "publish_gh" - "publish_pypi" - name: "Send notification to the Slack" runs-on: "ubuntu-24.04" env: SLACK_WEBHOOK_URL: "${{ secrets.SLACK_WEBHOOK_URL }}" @@ -209,8 +253,8 @@ jobs: - name: "Send a notification to Slack" # ENVs cannot be used directly in job.if. This is a workaround to check # if SLACK_WEBHOOK_URL is present. - if: "${{ env.SLACK_WEBHOOK_URL != '' }}" - uses: "slackapi/slack-github-action@v1.17.0" + if: "env.SLACK_WEBHOOK_URL != ''" + uses: "slackapi/slack-github-action@fcfb566f8b0aab22203f066d80ca1d7e4b5d05b3" # v1.27.1 with: payload: | { diff --git a/.gitignore b/.gitignore index c8dc455..cbb9133 100644 --- a/.gitignore +++ b/.gitignore @@ -297,6 +297,12 @@ fabric.properties # Rando creds.env +development/*.txt # Invoke overrides invoke.yml + +# Docs +docs/README.md +docs/CHANGELOG.md +public diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 0000000..b60d928 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,24 @@ +--- +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +# Set the version of Python in the build environment. +build: + os: "ubuntu-22.04" + tools: + python: "3.10" + +mkdocs: + configuration: "mkdocs.yml" + # fail_on_warning: true + +# Use our docs/requirements.txt during installation. +python: + install: + - requirements: "docs/requirements.txt" + - method: "pip" + path: "." diff --git a/.yamllint.yml b/.yamllint.yml index 58324ed..b68ca0f 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -1,10 +1,13 @@ --- extends: "default" rules: - comments: "enable" + comments: + min-spaces-from-content: 1 empty-values: "enable" indentation: indent-sequences: "consistent" line-length: "disable" quoted-strings: quote-type: "double" +ignore: | + .venv/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 6e4717b..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,12 +0,0 @@ -# CONTRIBUTING - -To contribute, follow this workflow. - -1. Open an issue -2. Get approval from one of the codeowners before working on the issue -3. If working on the issue, assign the issue to yourself -4. Open a PR into integration -5. Get peer review and approval to merge from one of the codeowners -6. Once approval has been gained, merge the PR into integration -7. Once the PR is merged, delete the branch -8. One of the codeowners will enumerate the features added per the contributer's PR when a tagged release is merged into master diff --git a/Dockerfile b/Dockerfile index c60a596..cb1757c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,26 @@ -ARG PYTHON_VER +ARG PYTHON_VER=3.11 FROM python:${PYTHON_VER}-slim -RUN pip install --upgrade pip \ - && pip install poetry +# Install Poetry manually via its installer script; +# if we instead used "pip install poetry" it would install its own dependencies globally which may conflict with ours. +# https://python-poetry.org/docs/master/#installing-with-the-official-installer +# This also makes it so that Poetry will *not* be included in the "final" image since it's not installed to /usr/local/ +ARG POETRY_HOME=/opt/poetry +ARG POETRY_INSTALLER_PARALLEL=true +ARG POETRY_VERSION=1.8.2 +ARG POETRY_VIRTUALENVS_CREATE=false +ADD https://install.python-poetry.org /tmp/install-poetry.py +RUN python /tmp/install-poetry.py + +# Add poetry install location to the $PATH +ENV PATH="${POETRY_HOME}/bin:${PATH}" + +RUN poetry config virtualenvs.create ${POETRY_VIRTUALENVS_CREATE} && \ + poetry config installer.parallel "${POETRY_INSTALLER_PARALLEL}" WORKDIR /local -COPY pyproject.toml /local +COPY . /local -RUN poetry config virtualenvs.create false \ - && poetry install --no-interaction --no-ansi +# Install the app +RUN poetry install --with dev diff --git a/README.md b/README.md index ecf27e8..85b04d6 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,37 @@ -# jdiff +# Jdiff -`jdiff` is a lightweight Python library allowing you to examine structured data. `jdiff` provides an interface to intelligently compare--via key presense/absense and value comparison--JSON data objects +

+ +
+ + + + +
+

-Our primary use case is the examination of structured data returned from networking devices, such as: +## Overview -* Compare the operational state of network devices pre and post change -* Compare operational state of a device vs a "known healthy" state -* Compare state of similar devices, such as a pair of leafs or a pair of backbone routers -* Compare operational state of a component (interface, vrf, bgp peering, etc.) migrated from one device to another +`jdiff` is a lightweight Python library allowing you to examine structured data. `jdiff` provides an interface to intelligently compare JSON data objects and test for the presence (or absence) of keys. You can also examine and compare corresponding key-values. -However, the library fits other use cases where structured data needs to be operated on. - -## Installation - -Install from PyPI: - -``` -pip install jdiff -``` +## Documentation -## Intelligent Comparison +Full web-based HTML documentation for this library can be found over on the [Jdiff Docs](https://jdiff.readthedocs.io) website: -The library provides the ability to ask more intelligent questions of a given data structure. Comparisons of data such as "Is my pre change state the same as my post change state", is not that interesting of a comparison. The library intends to ask intelligent questions _like_: +- [User Guide](https://jdiff.readthedocs.io/en/latest/user/lib_overview/) - Overview, Using the library, Getting Started. +- [Administrator Guide](https://jdiff.readthedocs.io/en/latest/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the library. +- [Developer Guide](https://jdiff.readthedocs.io/en/latest/dev/contributing/) - Extending the library, Code Reference, Contribution Guide. +- [Release Notes / Changelog](https://jdiff.readthedocs.io/en/latest/admin/release_notes/). +- [Frequently Asked Questions](https://jdiff.readthedocs.io/en/latest/user/faq/). -* Is the route table within 10% of routes before and after a change? -* Is all of the interfaces that were up before the change, still up? -* Are there at least 10k sessions of traffic on my firewall? -* Is there there at least 2 interfaces up within lldp neighbors? +### Contributing to the Docs -## Technical Overview +All the Markdown source for the library documentation can be found under the [docs](https://github.com/networktocode/jdiff/tree/develop/docs) folder in this repository. For simple edits, a Markdown capable editor is sufficient - clone the repository and edit away. -The library heavily relies on [JMESPath](https://jmespath.org/) for traversing the JSON object and finding the values to be evaluated. More on that [here](#customized-jmespath). +If you need to view the fully generated documentation site, you can build it with [mkdocs](https://www.mkdocs.org/). A container hosting the docs will be started using the invoke commands (details in the [Development Environment Guide](https://jdiff.readthedocs.io/en/latest/dev/dev_environment/#docker-development-environment)) on [http://localhost:8001](http://localhost:8001). As your changes are saved, the live docs will be automatically reloaded. -`jdiff` has been developed around diffing and testing structured data returned from Network APIs and libraries (such as TextFSM) but is equally useful when working or dealing with data returned from APIs. +Any PRs with fixes or improvements are very welcome! -## Documentation +## Questions -Documentation is hosted on Read the Docs at [jdiff Documentation](https://jdiff.readthedocs.io/). +For any questions or comments, please check the [FAQ](https://jdiff.readthedocs.io/en/latest/user/faq/) first. Feel free to also swing by the [Network to Code Slack](https://networktocode.slack.com/) (channel `#networktocode`), sign up [here](http://slack.networktocode.com/) if you don't have an account. diff --git a/docs/admin/install.md b/docs/admin/install.md new file mode 100644 index 0000000..124329a --- /dev/null +++ b/docs/admin/install.md @@ -0,0 +1,22 @@ +# Installation + +Option 1: Install from PyPI. + +```bash +pip install jdiff +``` + +Option 2: Manually install via Poetry. + +```bash +git clone https://github.com/networktocode/jdiff.git +cd jdiff +curl -sSL https://install.python-poetry.org | python3 - +poetry install +``` + +Option 3: Install from a GitHub branch, such as develop as shown below. + +```bash +pip install git+https://github.com/networktocode/jdiff.git@develop +``` diff --git a/docs/admin/release_notes/index.md b/docs/admin/release_notes/index.md new file mode 100644 index 0000000..12cb516 --- /dev/null +++ b/docs/admin/release_notes/index.md @@ -0,0 +1,3 @@ +# Release Notes + +All the published release notes can be found via the navigation menu. All patch releases are included in the same minor release (e.g. `v1.2`) document. diff --git a/CHANGELOG.md b/docs/admin/release_notes/version_0.0.md similarity index 97% rename from CHANGELOG.md rename to docs/admin/release_notes/version_0.0.md index 48d9367..9f79e4c 100644 --- a/CHANGELOG.md +++ b/docs/admin/release_notes/version_0.0.md @@ -1,4 +1,4 @@ -# Changelog +# v0.0 Release Notes ## v0.0.7 - Fix exclude to work with lists [#123](https://github.com/networktocode/jdiff/pull/123) diff --git a/docs/admin/release_notes/version_1.0.md b/docs/admin/release_notes/version_1.0.md new file mode 100644 index 0000000..60dfd5c --- /dev/null +++ b/docs/admin/release_notes/version_1.0.md @@ -0,0 +1,18 @@ +# v1.0 Release Notes + +This document describes all new features and changes in the release. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Release Overview + +- Updated the entire project to the new Network to Code development standards. +- Added a JSON data compliance utility to help reconstruct JSON from compliance results. + +## [v1.0.0 (2025-08-25)](https://github.com/networktocode/jdiff/releases/tag/v1.0.0) + +### Added + +- [#130](https://github.com/networktocode/jdiff/issues/130) - Add the ability to reconstruct JSON blobs to perform JSON data compliance. + +### Fixed + +- [#128](https://github.com/networktocode/jdiff/issues/128) - Fixed the development standards to use 2025 standards. diff --git a/docs/admin/uninstall.md b/docs/admin/uninstall.md new file mode 100644 index 0000000..c14848a --- /dev/null +++ b/docs/admin/uninstall.md @@ -0,0 +1,7 @@ +# Uninstall + +Uninstall from environment. + +```bash +$ pip uninstall jdiff +``` \ No newline at end of file diff --git a/docs/admin/upgrade.md b/docs/admin/upgrade.md new file mode 100644 index 0000000..77e0d55 --- /dev/null +++ b/docs/admin/upgrade.md @@ -0,0 +1,7 @@ +# Upgrading the Library + +Upgrade from PyPI. + +```bash +$ pip install jdiff --upgrade +``` diff --git a/docs/assets/extra.css b/docs/assets/extra.css new file mode 100644 index 0000000..50884f4 --- /dev/null +++ b/docs/assets/extra.css @@ -0,0 +1,152 @@ +:root>* { + --md-accent-fg-color: #ff8504; + --md-primary-fg-color: #ff8504; + --md-typeset-a-color: #0097ff; +} + +[data-md-color-scheme="slate"] { + --md-default-bg-color: hsla(var(--md-hue), 0%, 15%, 1); + --md-typeset-a-color: #0097ff; +} + +/* Accessibility: Increase fonts for dark theme */ +[data-md-color-scheme="slate"] .md-typeset { + font-size: 0.9rem; +} + +[data-md-color-scheme="slate"] .md-typeset table:not([class]) { + font-size: 0.7rem; +} + +.md-tabs__link { + font-size: 0.8rem; +} + +.md-tabs__link--active { + color: var(--md-primary-fg-color); +} + +.md-header__button.md-logo :is(img, svg) { + height: 2rem; +} + +.md-header__button.md-logo :-webkit-any(img, svg) { + height: 2rem; +} + +.md-header__title { + font-size: 1.2rem; +} + +img.logo { + height: 100px; +} + +img.copyright-logo { + height: 24px; + vertical-align: middle; +} + +[data-md-color-primary=black] .md-header { + background-color: #212121; +} + +@media screen and (min-width: 76.25em) { + [data-md-color-primary=black] .md-tabs { + background-color: #212121; + } +} + +/* Customization for mkdocstrings */ +/* Indentation. */ +div.doc-contents:not(.first) { + padding-left: 25px; + border-left: .2rem solid var(--md-typeset-table-color); +} + +/* Mark external links as such. */ +a.autorefs-external::after { + /* https://primer.style/octicons/arrow-up-right-24 */ + background-image: url('data:image/svg+xml,'); + content: ' '; + + display: inline-block; + position: relative; + top: 0.1em; + margin-left: 0.2em; + margin-right: 0.1em; + + height: 1em; + width: 1em; + border-radius: 100%; + background-color: var(--md-typeset-a-color); +} + +a.autorefs-external:hover::after { + background-color: var(--md-accent-fg-color); +} + + +/* Customization for mkdocs-version-annotations */ +:root { + /* Icon for "version-added" admonition: Material Design Icons "plus-box-outline" */ + --md-admonition-icon--version-added: url('data:image/svg+xml;charset=utf-8,'); + /* Icon for "version-changed" admonition: Material Design Icons "delta" */ + --md-admonition-icon--version-changed: url('data:image/svg+xml;charset=utf-8,'); + /* Icon for "version-removed" admonition: Material Design Icons "minus-circle-outline" */ + --md-admonition-icon--version-removed: url('data:image/svg+xml;charset=utf-8,'); +} + +/* "version-added" admonition in green */ +.md-typeset .admonition.version-added, +.md-typeset details.version-added { + border-color: rgb(0, 200, 83); +} + +.md-typeset .version-added>.admonition-title, +.md-typeset .version-added>summary { + background-color: rgba(0, 200, 83, .1); +} + +.md-typeset .version-added>.admonition-title::before, +.md-typeset .version-added>summary::before { + background-color: rgb(0, 200, 83); + -webkit-mask-image: var(--md-admonition-icon--version-added); + mask-image: var(--md-admonition-icon--version-added); +} + +/* "version-changed" admonition in orange */ +.md-typeset .admonition.version-changed, +.md-typeset details.version-changed { + border-color: rgb(255, 145, 0); +} + +.md-typeset .version-changed>.admonition-title, +.md-typeset .version-changed>summary { + background-color: rgba(255, 145, 0, .1); +} + +.md-typeset .version-changed>.admonition-title::before, +.md-typeset .version-changed>summary::before { + background-color: rgb(255, 145, 0); + -webkit-mask-image: var(--md-admonition-icon--version-changed); + mask-image: var(--md-admonition-icon--version-changed); +} + +/* "version-removed" admonition in red */ +.md-typeset .admonition.version-removed, +.md-typeset details.version-removed { + border-color: rgb(255, 82, 82); +} + +.md-typeset .version-removed>.admonition-title, +.md-typeset .version-removed>summary { + background-color: rgba(255, 82, 82, .1); +} + +.md-typeset .version-removed>.admonition-title::before, +.md-typeset .version-removed>summary::before { + background-color: rgb(255, 82, 82); + -webkit-mask-image: var(--md-admonition-icon--version-removed); + mask-image: var(--md-admonition-icon--version-removed); +} diff --git a/docs/assets/favicon.ico b/docs/assets/favicon.ico new file mode 100644 index 0000000..9685c83 Binary files /dev/null and b/docs/assets/favicon.ico differ diff --git a/docs/assets/networktocode_bw.png b/docs/assets/networktocode_bw.png new file mode 100644 index 0000000..075c492 Binary files /dev/null and b/docs/assets/networktocode_bw.png differ diff --git a/docs/assets/networktocode_logo.png b/docs/assets/networktocode_logo.png new file mode 100644 index 0000000..b2a3cba Binary files /dev/null and b/docs/assets/networktocode_logo.png differ diff --git a/docs/assets/networktocode_logo.svg b/docs/assets/networktocode_logo.svg new file mode 100644 index 0000000..348e524 --- /dev/null +++ b/docs/assets/networktocode_logo.svg @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/overrides/partials/copyright.html b/docs/assets/overrides/partials/copyright.html new file mode 100644 index 0000000..b623894 --- /dev/null +++ b/docs/assets/overrides/partials/copyright.html @@ -0,0 +1,20 @@ + + + + + +
diff --git a/docs/code-reference/jdiff/__init__.md b/docs/code-reference/jdiff/__init__.md new file mode 100644 index 0000000..b811229 --- /dev/null +++ b/docs/code-reference/jdiff/__init__.md @@ -0,0 +1 @@ +::: jdiff diff --git a/docs/code-reference/jdiff/check_types.md b/docs/code-reference/jdiff/check_types.md new file mode 100644 index 0000000..c516f6f --- /dev/null +++ b/docs/code-reference/jdiff/check_types.md @@ -0,0 +1 @@ +::: jdiff.check_types diff --git a/docs/code-reference/jdiff/evaluators.md b/docs/code-reference/jdiff/evaluators.md new file mode 100644 index 0000000..6fbf718 --- /dev/null +++ b/docs/code-reference/jdiff/evaluators.md @@ -0,0 +1 @@ +::: jdiff.evaluators diff --git a/docs/code-reference/jdiff/extract_data.md b/docs/code-reference/jdiff/extract_data.md new file mode 100644 index 0000000..76d8840 --- /dev/null +++ b/docs/code-reference/jdiff/extract_data.md @@ -0,0 +1 @@ +::: jdiff.extract_data diff --git a/docs/code-reference/jdiff/operator.md b/docs/code-reference/jdiff/operator.md new file mode 100644 index 0000000..ed20ee9 --- /dev/null +++ b/docs/code-reference/jdiff/operator.md @@ -0,0 +1 @@ +::: jdiff.operator diff --git a/docs/code-reference/jdiff/utils/__init__.md b/docs/code-reference/jdiff/utils/__init__.md new file mode 100644 index 0000000..9f026c8 --- /dev/null +++ b/docs/code-reference/jdiff/utils/__init__.md @@ -0,0 +1 @@ +::: jdiff.utils diff --git a/docs/code-reference/jdiff/utils/data_normalization.md b/docs/code-reference/jdiff/utils/data_normalization.md new file mode 100644 index 0000000..6f08a0b --- /dev/null +++ b/docs/code-reference/jdiff/utils/data_normalization.md @@ -0,0 +1 @@ +::: jdiff.utils.data_normalization diff --git a/docs/code-reference/jdiff/utils/diff_helpers.md b/docs/code-reference/jdiff/utils/diff_helpers.md new file mode 100644 index 0000000..9760b57 --- /dev/null +++ b/docs/code-reference/jdiff/utils/diff_helpers.md @@ -0,0 +1 @@ +::: jdiff.utils.diff_helpers diff --git a/docs/code-reference/jdiff/utils/jmespath_parsers.md b/docs/code-reference/jdiff/utils/jmespath_parsers.md new file mode 100644 index 0000000..aeee717 --- /dev/null +++ b/docs/code-reference/jdiff/utils/jmespath_parsers.md @@ -0,0 +1 @@ +::: jdiff.utils.jmespath_parsers diff --git a/docs/dev/arch_decision.md b/docs/dev/arch_decision.md new file mode 100644 index 0000000..cbe4d49 --- /dev/null +++ b/docs/dev/arch_decision.md @@ -0,0 +1,3 @@ +# Architecture Decision Records + +The intention is to document deviations from a standard pattern. diff --git a/docs/dev/contributing.md b/docs/dev/contributing.md new file mode 100644 index 0000000..58c0d89 --- /dev/null +++ b/docs/dev/contributing.md @@ -0,0 +1,77 @@ +# Contributing + +Pull requests are welcomed and automatically built and tested against multiple versions of Python through GitHub Actions. + +Except for unit tests, testing is only supported on Python 3.9. + +The project is packaged with a light development environment based on `Docker` to help with the local development of the project and to run tests within GitHub Actions. + +The project is following Network to Code software development guidelines and is leveraging the following: + +- Python linting and formatting: `pylint` and `ruff`. +- YAML linting is done with `yamllint`. + +Documentation is built using [mkdocs](https://www.mkdocs.org/). The [Docker based development environment](dev_environment.md#docker-development-environment) can be started by running `invoke docs` [http://localhost:8001](http://localhost:8001) that auto-refreshes when you make any changes to your local files. + +## Creating Changelog Fragments + +All pull requests to `next` or `develop` must include a changelog fragment file in the `./changes` directory. To create a fragment, use your GitHub issue number and fragment type as the filename. For example, `2362.added`. Valid fragment types are `added`, `changed`, `deprecated`, `fixed`, `removed`, and `security`. The change summary is added to the file in plain text. Change summaries should be complete sentences, starting with a capital letter and ending with a period, and be in past tense. Each line of the change fragment will generate a single change entry in the release notes. Use multiple lines in the same file if your change needs to generate multiple release notes in the same category. If the change needs to create multiple entries in separate categories, create multiple files. + +!!! example + + **Wrong** + ```plaintext title="changes/1234.fixed" + fix critical bug in documentation + ``` + + **Right** + ```plaintext title="changes/1234.fixed" + Fixed critical bug in documentation. + ``` + +!!! example "Multiple Entry Example" + + This will generate 2 entries in the `fixed` category and one entry in the `changed` category. + + ```plaintext title="changes/1234.fixed" + Fixed critical bug in documentation. + Fixed release notes generation. + ``` + + ```plaintext title="changes/1234.changed" + Changed release notes generation. + ``` + +## Branching Policy + +The branching policy includes the following tenets: + +- The develop branch is the primary branch to develop off of. +- If there is a reason to have a patch version, the maintainers may use cherry-picking strategy. +- PRs intended to add new features should be sourced from the develop branch. +- PRs intended to address bug fixes and security patches should be sourced from the develop branch. +- PRs intended to add new features that break backward compatibility should be discussed before a PR is created. + +Jdiff will observe semantic versioning, as of 1.0. This may result in an quick turn around in minor versions to keep pace with an ever growing feature set. + +## Release Policy + +Jdiff has currently no intended scheduled release schedule, and will release new features in minor versions. + +When a new release is created the following should happen. + +- A release PR is created with: + - Update to the changelog in `docs/admin/release_notes/version_..md` file to reflect the changes. + - Change the version from `..-beta` to `..` in pyproject.toml. + - Set the PR to the main +- Ensure the tests for the PR pass. +- Merge the PR. +- Create a new tag: + - The tag should be in the form of `v..`. + - The title should be in the form of `v..`. + - The description should be the changes that were added to the `version_..md` document. +- If merged into `main`, then push from `main` to `develop`, in order to retain the merge commit created when the PR was merged +- A post release PR is created with. + - Change the version from `..` to `..-beta` pyproject.toml. + - Set the PR to the `develop`. + - Once tests pass, merge. diff --git a/docs/dev/dev_environment.md b/docs/dev/dev_environment.md new file mode 100644 index 0000000..5684fc0 --- /dev/null +++ b/docs/dev/dev_environment.md @@ -0,0 +1,98 @@ +# Building Your Development Environment + +## Quickstart + +The development environment can be used in two ways: + +1. `Recommended` All services are spun up using Docker and a local mount so you can develop locally, but Jdiff is spun up within the Docker container. +2. With a local poetry environment if you wish to develop outside of Docker. + +This is a quick reference guide if you're already familiar with the development environment provided, which you can read more about later in this document. + +### Invoke + +The [Invoke](http://www.pyinvoke.org/) library is used to provide some helper commands based on the environment. There are a few configuration parameters which can be passed to Invoke to override the default configuration: + +- `local`: a boolean flag indicating if invoke tasks should be run on the host or inside the docker containers (default: False, commands will be run in docker containers) + +Using **Invoke** these configuration options can be overridden using [several methods](https://docs.pyinvoke.org/en/stable/concepts/configuration.html). Perhaps the simplest is setting an environment variable `INVOKE_JDIFF_VARIABLE_NAME` where `VARIABLE_NAME` is the variable you are trying to override. There is an example `invoke.yml` (`invoke.example.yml`) in this directory which can be used as a starting point. + +### Docker Development Environment + +!!! tip + This is the recommended option for development. + +This project is managed by [Python Poetry](https://python-poetry.org/) and has a few requirements to setup your development environment: + +1. Install Poetry, see the [Poetry Documentation](https://python-poetry.org/docs/#installation) for your operating system. +2. Install Docker, see the [Docker documentation](https://docs.docker.com/get-docker/) for your operating system. + +Once you have Poetry and Docker installed you can run the following commands (in the root of the repository) to install all other development dependencies in an isolated Python virtual environment: + +```shell +poetry shell +poetry install +invoke build +invoke start +``` + +Live documentation can be viewed at [http://localhost:8001](http://localhost:8001). + +To either stop or destroy the development environment use the following options. + +- **invoke stop** - Stop the containers, but keep all underlying systems intact +- **invoke destroy** - Stop and remove all containers, volumes, etc. (This results in data loss due to the volume being deleted) + +## Poetry + +Poetry is used in lieu of the "virtualenv" commands and is leveraged in both environments. The virtual environment will provide all of the Python packages required to manage the development environment such as **Invoke**. See the [Local Development Environment](#local-poetry-development-environment) section to see how to install Jdiff if you're going to be developing locally (i.e. not using the Docker container). + +The `pyproject.toml` file outlines all of the relevant dependencies for the project: + +- `tool.poetry.dependencies` - the main list of dependencies. +- `tool.poetry.group.dev.dependencies` - development dependencies, to facilitate linting, testing, and documentation building. + +The `poetry shell` command is used to create and enable a virtual environment managed by Poetry, so all commands ran going forward are executed within the virtual environment. This is similar to running the `source venv/bin/activate` command with virtualenvs. To install project dependencies in the virtual environment, you should run `poetry install` - this will install **both** project and development dependencies. + +For more details about Poetry and its commands please check out its [online documentation](https://python-poetry.org/docs/). + +## Full Docker Development Environment + +This project is set up with a number of **Invoke** tasks consumed as simple CLI commands to get developing fast. You'll use a few `invoke` commands to get your environment up and running. + +## CLI Helper Commands + +The project features a CLI helper based on [invoke](http://www.pyinvoke.org/) to help setup the development environment. The commands are listed below in 3 categories: +- `dev environment` +- `utility` +- `testing` + +Each command can be executed with `invoke `. Each command also has its own help `invoke --help` + +### Local dev environment + +``` + build Build all docker images. + clean Remove the project specific image. + docs Build and serve docs locally. + rebuild Clean the Docker image and then rebuild without using cache. +``` + +### Utility + +``` + cli Enter the image to perform troubleshooting or dev work. + clean Remove stopped containers that source for image `Jdiff:` + generate-release-notes Generate Release Notes using Towncrier. +``` + +### Testing + +``` + autoformat (a) Run code autoformatting. + pylint Run pylint for the specified name and Python version. + ruff Run ruff to perform code formatting and/or linting. + pytest Run pytest for the specified name and Python version. + tests Run all tests for the specified name and Python version. + yamllint Run yamllint to validate formatting adheres to NTC defined YAML standards. +``` \ No newline at end of file diff --git a/docs/dev/extending.md b/docs/dev/extending.md new file mode 100644 index 0000000..e974b82 --- /dev/null +++ b/docs/dev/extending.md @@ -0,0 +1,4 @@ +# Extending the Library + +Extending the library is welcome, however it is best to open an issue first, to ensure that a PR would be accepted and makes sense in terms of features and design. + diff --git a/docs/development.md b/docs/development.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/example.md b/docs/example.md deleted file mode 100644 index e69de29..0000000 diff --git a/docs/extra.css b/docs/extra.css deleted file mode 100644 index 85d51a1..0000000 --- a/docs/extra.css +++ /dev/null @@ -1,19 +0,0 @@ -/* Images */ -img { - display: block; - margin-left: auto; - margin-right: auto; -} - -/* Tables */ -table { - margin-bottom: 24px; - width: 100%; -} -th { - background-color: #f0f0f0; - padding: 6px; -} -td { - padding: 6px; -} \ No newline at end of file diff --git a/docs/generate_code_reference_pages.py b/docs/generate_code_reference_pages.py new file mode 100644 index 0000000..636ab53 --- /dev/null +++ b/docs/generate_code_reference_pages.py @@ -0,0 +1,20 @@ +"""Generate code reference pages.""" + +from pathlib import Path + +import mkdocs_gen_files + +for file_path in Path("jdiff").rglob("*.py"): + module_path = file_path.with_suffix("") + doc_path = file_path.with_suffix(".md") + full_doc_path = Path("code-reference", doc_path) + + parts = list(module_path.parts) + if parts[-1] == "__init__": + parts = parts[:-1] + + with mkdocs_gen_files.open(full_doc_path, "w") as fd: + IDENTIFIER = ".".join(parts) + print(f"::: {IDENTIFIER}", file=fd) + + mkdocs_gen_files.set_edit_path(full_doc_path, file_path) diff --git a/docs/images/jdiff_logo.png b/docs/images/jdiff_logo.png new file mode 100644 index 0000000..032a750 Binary files /dev/null and b/docs/images/jdiff_logo.png differ diff --git a/docs/index.md b/docs/index.md index efe2626..f32fd72 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,78 +1,6 @@ -# jdiff +--- +hide: + - navigation +--- -`jdiff` is a lightweight Python library allowing you to examine structured data. `jdiff` provides an interface to intelligently compare JSON data objects and test for the presence (or absence) of keys. You can also examine and compare corresponding key-values. - -The library heavily relies on [JMESPath](https://jmespath.org/) for traversing the JSON object and finding the values to be evaluated. More on that [here](architecture.md#customized-jmespath). - -## Getting Started - - -First, you import the `CheckType` class. - -```python -from jdiff import CheckType -``` - -Get (or fabricate) some data. (This data may also be loaded from a file or from a string, more examples later.) - -```python -a = {"foo": "bar"} -b = {"foo": "bar baz"} -``` - -Call the `create` method of the `CheckType` class to get an instance of the check type you want to perform. - -```python -match = CheckType.create("exact_match") -``` - -Evaluate the check type and the diff. -```python -match.evaluate(a, b) ->>> ({'foo': {'new_value': 'bar baz', 'old_value': 'bar'}}, False) -``` - -This results in a tuple: -- The first value is the diff between the two data structures. -- The second value is a boolean with the result of the check. - -This diff can also show whether any keys were added or deleted. -The second value returned will be the boolean result of the check. In this case, the two data structures were not an exact match. - - -## Checking Data Structures - -As shown in the example, the check evaluation both performs a diff and tests the objects. All of the concrete `CheckTypes` both perform the diff and their specified check. - -More on the **check** part: The check provides a way to test some keys or values in our collected data. The check portion is focused on providing a boolean result of the test. There are a few different ways to check our data. - -Below are the names of checks provided by the library. These both describe the type of check performed against the data and are used as an argument to instantiate that type of check with the `create` method, e.g. `CheckType.create("check_type")`. - -- `exact_match`: the keys and values must match exactly between the two objects -- `tolerance`: the keys must match and the values can differ according to the 'tolerance' value provided -- `parameter_match`: a reference key and value is provided and its presence (or absence) is checked in the provided object -- `regex`: a reference regex pattern is provided and is used to find a match in the provided object -- `operator`: similar to parameter match, but the reference includes several different possible operators: 'in', 'bool', 'string', and numerical comparison with 'int' and 'float' to check against - -`CheckTypes` are explained in more detail in the [architecture](architecture.md). - - -## Workflow - -| ![jdiff Workflow](./images/jdiff-workflow.png) | -|:---:| -| **`jdiff` Workflow** | - - -1. The reference state object is retrieved or assembled. The structured data may be from: - - - an API - - another Python module/library - - retrieved from a saved file - - constructed programmatically - -2. Some time passes where some change to the data may occur; then the comparison state is retrieved or assembled, often using a similar process used to get the reference state. -3. The reference state is then compared to the current state via the jdiff library using one of the `CheckTypes`. -4. The evaluate method is called on the `check` object, and the result is returned. - -Please see [usage](usage.md) for commands and more information. +--8<-- "README.md" diff --git a/docs/requirements.txt b/docs/requirements.txt index 2fa8c99..e5187d1 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,13 @@ -mkdocs==1.2.4 +mkdocs==1.6.1 +markdown-data-tables==1.0.0 +markdown-version-annotations==1.0.1 +mkdocs-gen-files==0.5.0 +mkdocs-glightbox==0.4.0 +mkdocs-macros-plugin==1.3.7 +mkdocs-material==9.6.15 +mkdocs-redirects==1.2.2 +mkdocs-section-index==0.3.10 +mkdocs-redirects==1.2.2 +mkdocs-section-index==0.3.10 +mkdocstrings==0.27.0 +mkdocstrings-python==1.13.0 diff --git a/docs/architecture.md b/docs/user/architecture.md similarity index 98% rename from docs/architecture.md rename to docs/user/architecture.md index c348c11..b5accb3 100644 --- a/docs/architecture.md +++ b/docs/user/architecture.md @@ -1,6 +1,6 @@ # Library Architecture -| ![jdiff HLD](./images/hld-architecture.png) | +| ![jdiff HLD](../images/hld-architecture.png) | |:---:| | **`jdiff` architecture** | diff --git a/docs/user/faq.md b/docs/user/faq.md new file mode 100644 index 0000000..318b08d --- /dev/null +++ b/docs/user/faq.md @@ -0,0 +1 @@ +# Frequently Asked Questions diff --git a/docs/user/lib_getting_started.md b/docs/user/lib_getting_started.md new file mode 100644 index 0000000..99e45d6 --- /dev/null +++ b/docs/user/lib_getting_started.md @@ -0,0 +1,72 @@ +## Getting Started + + +First, you import the `CheckType` class. + +```python +from jdiff import CheckType +``` + +Get (or fabricate) some data. (This data may also be loaded from a file or from a string, more examples later.) + +```python +a = {"foo": "bar"} +b = {"foo": "bar baz"} +``` + +Call the `create` method of the `CheckType` class to get an instance of the check type you want to perform. + +```python +match = CheckType.create("exact_match") +``` + +Evaluate the check type and the diff. +```python +match.evaluate(a, b) +>>> ({'foo': {'new_value': 'bar baz', 'old_value': 'bar'}}, False) +``` + +This results in a tuple: +- The first value is the diff between the two data structures. +- The second value is a boolean with the result of the check. + +This diff can also show whether any keys were added or deleted. +The second value returned will be the boolean result of the check. In this case, the two data structures were not an exact match. + + +## Checking Data Structures + +As shown in the example, the check evaluation both performs a diff and tests the objects. All of the concrete `CheckTypes` both perform the diff and their specified check. + +More on the **check** part: The check provides a way to test some keys or values in our collected data. The check portion is focused on providing a boolean result of the test. There are a few different ways to check our data. + +Below are the names of checks provided by the library. These both describe the type of check performed against the data and are used as an argument to instantiate that type of check with the `create` method, e.g. `CheckType.create("check_type")`. + +- `exact_match`: the keys and values must match exactly between the two objects +- `tolerance`: the keys must match and the values can differ according to the 'tolerance' value provided +- `parameter_match`: a reference key and value is provided and its presence (or absence) is checked in the provided object +- `regex`: a reference regex pattern is provided and is used to find a match in the provided object +- `operator`: similar to parameter match, but the reference includes several different possible operators: 'in', 'bool', 'string', and numerical comparison with 'int' and 'float' to check against + +`CheckTypes` are explained in more detail in the [architecture](architecture.md). + + +## Workflow + +| ![jdiff Workflow](../images/jdiff-workflow.png) | +|:---:| +| **`jdiff` Workflow** | + + +1. The reference state object is retrieved or assembled. The structured data may be from: + + - an API + - another Python module/library + - retrieved from a saved file + - constructed programmatically + +2. Some time passes where some change to the data may occur; then the comparison state is retrieved or assembled, often using a similar process used to get the reference state. +3. The reference state is then compared to the current state via the jdiff library using one of the `CheckTypes`. +4. The evaluate method is called on the `check` object, and the result is returned. + +Please see [usage](usage.md) for commands and more information. diff --git a/docs/user/lib_overview.md b/docs/user/lib_overview.md new file mode 100644 index 0000000..46a00dc --- /dev/null +++ b/docs/user/lib_overview.md @@ -0,0 +1,5 @@ +# Library Overview + +`jdiff` is a lightweight Python library allowing you to examine structured data. `jdiff` provides an interface to intelligently compare JSON data objects and test for the presence (or absence) of keys. You can also examine and compare corresponding key-values. + +The library heavily relies on [JMESPath](https://jmespath.org/) for traversing the JSON object and finding the values to be evaluated. More on that [here](architecture.md#customized-jmespath). diff --git a/docs/usage.md b/docs/user/usage.md similarity index 86% rename from docs/usage.md rename to docs/user/usage.md index ce7692a..7ebdfd3 100644 --- a/docs/usage.md +++ b/docs/user/usage.md @@ -610,3 +610,118 @@ Can you guess what would be the outcome for an `int`, `float` operator? ``` See `tests` folder in the repo for more examples. + +## Putting a Result Back Together + +Jdiff results are very helpful in determining what is wrong with the outputs. What if you want to reconstruct the results in order to fix the problem. The `parse_diff` helper does just that. Imagine you have a `jdiff` result such as: + +```python +ex1 = {'bar-2': 'missing', 'bar-1': 'new'} +ex2 = { + 'hostname': {'new_value': 'veos-actual', 'old_value': 'veos-intended'}, + 'domain-name': 'new' + } +ex3 = { + 'hostname': {'new_value': 'veos-0', 'old_value': 'veos'}, + "index_element['ip name']": 'missing', + 'domain-name': 'new' + } +ex4 = { + 'servers': + { + 'server': defaultdict(, + { + 'missing': [ + { + 'address': '1.us.pool.ntp.org', + 'config': {'address': '1.us.pool.ntp.org'}, + 'state': {'address': '1.us.pool.ntp.org'} + } + ] + } + ) + } + } +``` + +And you need to understand what is extra and what is missing from the result. (Think configuration compliance on a JSON/JSON-RPC system). + +Well running the `parse_diff` will give you what is extra (in the comparison data) and missing from the reference data, and also the reverse. What is missing (in the reference data) that is missing from the comparison data. + +An example will help visualize the results. + +```python +In [1]: from jdiff import extract_data_from_json + ...: from jdiff.check_types import CheckType + ...: from jdiff.utils.diff_helpers import parse_diff + +In [2]: reference_data = {"foo": {"bar-2": "baz2"}} + ...: comparison_data = {"foo": {"bar-1": "baz1"}} + ...: match_key = "foo" + +In [3]: extracted_comparison_data = extract_data_from_json(comparison_data, match_key) + +In [4]: extracted_comparison_data +Out[4]: {'bar-1': 'baz1'} + +In [5]: extracted_reference_data = extract_data_from_json(reference_data, match_key) + +In [6]: extracted_reference_data +Out[6]: {'bar-2': 'baz2'} + +In [7]: jdiff_exact_match = CheckType.create("exact_match") + ...: jdiff_evaluate_response, _ = jdiff_exact_match.evaluate(extracted_reference_data, extracted_comparison_data) + +In [8]: jdiff_evaluate_response +Out[8]: {'bar-2': 'missing', 'bar-1': 'new'} + +In [9]: parsed_extra, parsed_missing = parse_diff( + ...: jdiff_evaluate_response, + ...: comparison_data, + ...: reference_data, + ...: match_key, + ...: ) + ...: + +In [10]: parsed_extra +Out[10]: {'bar-1': 'baz1'} + +In [10]: parsed_missing +Out[10]: {'bar-2': 'baz2'} +``` + +What about one with a more true JSON data structure. Like this RESTCONF YANG response. + +```python +from jdiff import extract_data_from_json +from jdiff.check_types import CheckType +from jdiff.utils.diff_helpers import parse_diff + +reference_data = {"openconfig-system:config": {"hostname": "veos", "ip name": "ntc.com"}} +comparison_data = {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos-0"}} +match_key = '"openconfig-system:config"' +extracted_comparison_data = extract_data_from_json(comparison_data, match_key) +extracted_reference_data = extract_data_from_json(reference_data, match_key) +jdiff_exact_match = CheckType.create("exact_match") +jdiff_evaluate_response, _ = jdiff_exact_match.evaluate(extracted_reference_data, extracted_comparison_data) + +parsed_extra, parsed_missing = parse_diff( + jdiff_evaluate_response, + comparison_data, + reference_data, + match_key, +) +``` +Which results in: + +```python +In [24]: parsed_extra +{'hostname': 'veos-0', 'domain-name': 'ntc.com'} + +In [25]: parsed_missing +Out[25]: {'hostname': 'veos', 'ip name': 'ntc.com'} +``` + +Now you can see how valuable this data can be to reconstruct, or remediate a out of compliant JSON object. + +For more detailed examples see the `test_diff_helpers.py` file. diff --git a/example.invoke.yml b/example.invoke.yml new file mode 100644 index 0000000..fc5bb7b --- /dev/null +++ b/example.invoke.yml @@ -0,0 +1,7 @@ +--- +jdiff: + python_ver: "3.11" + # local: false + # image_name: "jdiff" + # image_ver: "latest" + # pwd: "." diff --git a/jdiff/__init__.py b/jdiff/__init__.py index 3d0a62b..37e2b6b 100644 --- a/jdiff/__init__.py +++ b/jdiff/__init__.py @@ -1,4 +1,5 @@ """Pre/Post Check library.""" + from .check_types import CheckType from .extract_data import extract_data_from_json diff --git a/jdiff/check_types.py b/jdiff/check_types.py index 8b1bd0d..164159f 100644 --- a/jdiff/check_types.py +++ b/jdiff/check_types.py @@ -1,7 +1,9 @@ """CheckType Implementation.""" -from typing import List, Tuple, Dict, Any, Union + from abc import ABC, abstractmethod -from .evaluators import diff_generator, parameter_evaluator, regex_evaluator, operator_evaluator +from typing import Any, Dict, List, Tuple, Union + +from .evaluators import diff_generator, operator_evaluator, parameter_evaluator, regex_evaluator # pylint: disable=arguments-differ @@ -35,8 +37,8 @@ def evaluate(self, *args, **kwargs) -> Tuple[Dict, bool]: This method is the one that each CheckType has to implement. Args: - *args: arguments specific to child class implementation - **kwargs: named arguments + *args (tuple): arguments specific to child class implementation + **kwargs (dict): named arguments Returns: tuple: Dictionary representing check result, bool indicating if differences are found. diff --git a/jdiff/evaluators.py b/jdiff/evaluators.py index 6b9474a..d611994 100644 --- a/jdiff/evaluators.py +++ b/jdiff/evaluators.py @@ -1,9 +1,12 @@ """Evaluators.""" + import re -from typing import Any, Mapping, Dict, Tuple, List +from typing import Any, Dict, List, Mapping, Tuple + from deepdiff import DeepDiff -from .utils.diff_helpers import get_diff_iterables_items, fix_deepdiff_key_names + from .operator import Operator +from .utils.diff_helpers import fix_deepdiff_key_names, get_diff_iterables_items def diff_generator(pre_result: Any, post_result: Any) -> Dict: @@ -42,6 +45,7 @@ def parameter_evaluator(values: List[Dict], parameters: Mapping, mode: str) -> D Args: values: List of items what we will check the parameters against parameters: Dict with the keys and reference values to check + mode: "match" or "no-match" to define the evaluation mode Example: values: [{'7.7.7.7': {'peerAddress': '7.7.7.7', 'localAsn': '65130.1100', 'linkType': 'external'}}] diff --git a/jdiff/extract_data.py b/jdiff/extract_data.py index a4a57d0..9190401 100644 --- a/jdiff/extract_data.py +++ b/jdiff/extract_data.py @@ -1,13 +1,16 @@ """Extract data from JSON. Based on custom JMSPath implementation.""" + import re import warnings -from typing import Mapping, List, Dict, Any, Union, Optional +from typing import Any, Dict, List, Mapping, Optional, Union + import jmespath + from .utils.data_normalization import exclude_filter, flatten_list from .utils.jmespath_parsers import ( - jmespath_value_parser, - jmespath_refkey_parser, associate_key_of_my_value, + jmespath_refkey_parser, + jmespath_value_parser, keys_values_zipper, multi_reference_keys, ) @@ -48,7 +51,10 @@ def extract_data_from_json(data: Union[Mapping, List], path: str = "*", exclude: if len(re.findall(r"\$.*?\$", path)) > 1: clean_path = path.replace("$", "") values = jmespath.search(f"{clean_path}{' | []' * (path.count('*') - 1)}", data) - return keys_values_zipper(multi_reference_keys(path, data), associate_key_of_my_value(clean_path, values)) + return keys_values_zipper( + multi_reference_keys(path, data), + associate_key_of_my_value(clean_path, values), + ) values = jmespath.search(jmespath_value_parser(path), data) diff --git a/jdiff/operator.py b/jdiff/operator.py index 6c07000..ffa3921 100644 --- a/jdiff/operator.py +++ b/jdiff/operator.py @@ -1,4 +1,5 @@ """Operator diff.""" + import operator from typing import Any, List, Tuple diff --git a/jdiff/utils/__init__.py b/jdiff/utils/__init__.py index e69de29..81652e7 100644 --- a/jdiff/utils/__init__.py +++ b/jdiff/utils/__init__.py @@ -0,0 +1 @@ +"""Utils for jdiff.""" diff --git a/jdiff/utils/data_normalization.py b/jdiff/utils/data_normalization.py index 8a5e053..a862c6a 100644 --- a/jdiff/utils/data_normalization.py +++ b/jdiff/utils/data_normalization.py @@ -1,5 +1,6 @@ """Data Normalization utilities.""" -from typing import List, Generator, Union, Dict + +from typing import Dict, Generator, List, Union def flatten_list(my_list: List) -> List: diff --git a/jdiff/utils/diff_helpers.py b/jdiff/utils/diff_helpers.py index a22c4a6..706961e 100644 --- a/jdiff/utils/diff_helpers.py +++ b/jdiff/utils/diff_helpers.py @@ -1,8 +1,10 @@ """Diff helpers.""" + import re from collections import defaultdict -from functools import partial -from typing import Mapping, Dict, List, DefaultDict +from functools import partial, reduce +from operator import getitem +from typing import DefaultDict, Dict, List, Mapping REGEX_PATTERN_RELEVANT_KEYS = r"'([A-Za-z0-9_\./\\-]*)'" @@ -11,10 +13,10 @@ def get_diff_iterables_items(diff_result: Mapping) -> DefaultDict: """Helper function for diff_generator to postprocess changes reported by DeepDiff for iterables. DeepDiff iterable_items are returned when the source data is a list - and provided in the format: "root['Ethernet3'][1]" - or more generically: root['KEY']['KEY']['KEY']...[numeric_index] + and provided in the format: `"root['Ethernet3'][1]"` + or more generically: `root['KEY']['KEY']['KEY']...[numeric_index]` where the KEYs are dict keys within the original object - and the "[index]" is appended to indicate the position within the list. + and the `"[index]"` is appended to indicate the position within the list. Args: diff_result: iterable comparison result from DeepDiff @@ -50,10 +52,12 @@ def fix_deepdiff_key_names(obj: Mapping) -> Dict: Args: obj (Mapping): Mapping to be fixed. For example: + ``` { "root[3]['7.7.7.7']['is_enabled']": {'new_value': False, 'old_value': True}, "root[3]['7.7.7.7']['is_up']": {'new_value': False, 'old_value': True} } + ``` Returns: Dict: aggregated output, for example: {'7.7.7.7': {'is_enabled': {'new_value': False, 'old_value': True}, @@ -85,3 +89,101 @@ def dict_merger(original_dict: Dict, dict_to_merge: Dict): original_dict[key + "_dup!"] = dict_to_merge[key] # avoid overwriting existing keys. else: original_dict[key] = dict_to_merge[key] + + +def _parse_index_element_string(index_element_string): + """Build out dictionary from the index element string.""" + result = {} + pattern = r"\[\'(.*?)\'\]" + match = re.findall(pattern, index_element_string) + if match: + for inner_key in match[1::]: + result[inner_key] = "" + return match, result + + +def set_nested_value(data, keys, value): + """ + Recursively sets a value in a nested dictionary, given a list of keys. + + Args: + data (dict): The nested dictionary to modify. + keys (list): A list of keys to access the target value. + value (str): The value to set. + + Returns: + None (None): The function modifies the dictionary in place. Returns None. + """ + if not keys: + return # Should not happen, but good to have. + if len(keys) == 1: + data[keys[0]] = value + else: + if keys[0] not in data: + data[keys[0]] = {} # Create the nested dictionary if it doesn't exist + set_nested_value(data[keys[0]], keys[1:], value) + + +def parse_diff(jdiff_evaluate_response, actual, intended, match_config): + """Parse jdiff evaluate result into missing and extra dictionaries. + + Dict value in jdiff_evaluate_response can be: + - 'missing' -> In the intended but missing from actual. + - 'new' -> In the actual missing from intended. + + Examples of jdiff_evaluate_response: + - {'bar-2': 'missing', 'bar-1': 'new'} + - {'hostname': {'new_value': 'veos-actual', 'old_value': 'veos-intended'}, 'domain-name': 'new'} + - {'hostname': {'new_value': 'veos-0', 'old_value': 'veos'}, "index_element['ip name']": 'missing', 'domain-name': 'new'} + - {'servers': {'server': defaultdict(, {'missing': [{'address': '1.us.pool.ntp.org', 'config': {'address': '1.us.pool.ntp.org'}, 'state': {'address': '1.us.pool.ntp.org'}}]})}} + """ + # Remove surrounding double quotes if present from jmespath/config-to-match match with - in the string. + match_config = match_config.strip('"') + extra = {} # In the actual missing from intended. + missing = {} # In the intended but missing from actual. + + def process_diff(_map, extra_map, missing_map, previous_key=None): + """Process the diff recursively.""" + for key, value in _map.items(): + if isinstance(value, dict) and all(nested_key in value for nested_key in ("new_value", "old_value")): + extra_map[key] = value["new_value"] + missing_map[key] = value["old_value"] + elif isinstance(value, str): + if "missing" in value and "index_element" in key: + key_chain, _ = _parse_index_element_string(key) + if len(key_chain) == 1: + missing_map[key_chain[0]] = intended.get(match_config, {}).get(key_chain[0]) + else: + new_value = reduce(getitem, key_chain, intended) + set_nested_value(extra_map, key_chain[1::], new_value) + elif "missing" in value: + missing_map[key] = intended.get(match_config, {}).get(key) + else: + if "new" in value: + extra_map[key] = actual.get(match_config, {}).get(key) + elif isinstance(value, defaultdict): + value_dict = dict(value) + if "new" in value_dict: + extra_map[previous_key][key] = value_dict.get("new", {}) + if "missing" in value_dict: + missing_map[previous_key][key] = value_dict.get("missing", {}) + elif isinstance(value, dict): + extra_map[key] = {} + missing_map[key] = {} + process_diff(value, extra_map, missing_map, previous_key=key) + return extra_map, missing_map + + extras, missing = process_diff(jdiff_evaluate_response, extra, missing) + # Don't like this, but with less the performant way of doing it right now it works to clear out + # Any empty dicts that are left over from the diff. + final_extras = extras.copy() + final_missing = missing.copy() + for key, value in extras.items(): + if isinstance(value, dict): + if not value: + del final_extras[key] + for key, value in missing.items(): + if isinstance(value, dict): + if not value: + del final_missing[key] + return final_extras, final_missing diff --git a/jdiff/utils/jmespath_parsers.py b/jdiff/utils/jmespath_parsers.py index ee1e669..d9c8644 100644 --- a/jdiff/utils/jmespath_parsers.py +++ b/jdiff/utils/jmespath_parsers.py @@ -5,13 +5,14 @@ From one expression defined in jdiff, we will derive two expressions: one expression that traverse the json output and get the evaluated bit of it, the second will target the reference key relative to the value to evaluate. More on README.md """ + import re -from typing import Mapping, List, Union +from typing import List, Mapping, Union import jmespath -def jmespath_value_parser(path: str): +def jmespath_value_parser(path: str) -> str: """ Extract the jmespath value path from 'path' argument. @@ -46,7 +47,7 @@ def jmespath_value_parser(path: str): return path -def jmespath_refkey_parser(path: str): +def jmespath_refkey_parser(path: str) -> str: """ Get the jmespath reference key path from 'path' argument. @@ -130,14 +131,14 @@ def keys_values_zipper(list_of_reference_keys: List, wanted_value_with_key: List return final_result -def multi_reference_keys(jmspath, data): +def multi_reference_keys(jmspath: str, data): """Build a list of concatenated reference keys. Args: - jmspath: "$*$.peers.$*$.*.ipv4.[accepted_prefixes]" - data: tests/mock/napalm_get_bgp_neighbors/multi_vrf.json + jmspath (str): "$*$.peers.$*$.*.ipv4.[accepted_prefixes]" + data (dict): tests/mock/napalm_get_bgp_neighbors/multi_vrf.json - Returns: + Returns (str): ["global.10.1.0.0", "global.10.2.0.0", "global.10.64.207.255", "global.7.7.7.7", "vpn.10.1.0.0", "vpn.10.2.0.0"] """ ref_key_regex = re.compile(r"\$.*?\$") diff --git a/mkdocs.yml b/mkdocs.yml index 3a66157..031c194 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,29 +1,151 @@ --- dev_addr: "127.0.0.1:8001" -edit_uri: "edit/docs" -site_name: "jdiff Documentation" +edit_uri: "edit/main/jdiff/docs" +site_dir: "jdiff/static/jdiff/docs" +site_name: "Jdiff Documentation" site_url: "https://jdiff.readthedocs.io/" -site_dir: "jdiff/docs" repo_url: "https://github.com/networktocode-llc/jdiff" -python: - install: - - requirements: "docs/requirements.txt" +copyright: "Copyright © The Authors" theme: - name: "readthedocs" + name: "material" navigation_depth: 4 + custom_dir: "docs/assets/overrides" hljs_languages: - - "django" + - "python" - "yaml" + features: + - "content.code.annotate" + - "content.code.copy" + - "content.tabs.link" + - "navigation.footer" + - "navigation.tabs" + - "navigation.tabs.sticky" + - "navigation.tracking" + - "search.highlight" + - "search.share" + - "search.suggest" + favicon: "assets/favicon.ico" + logo: "assets/networktocode_logo.svg" + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: "default" + primary: "black" + toggle: + icon: "material/weather-sunny" + name: "Switch to dark mode" + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: "slate" + primary: "black" + toggle: + icon: "material/weather-night" + name: "Switch to light mode" + +validation: + absolute_links: "warn" + anchors: "warn" + omitted_files: "warn" + unrecognized_links: "warn" + extra_css: - - "extra.css" + - "assets/extra.css" + +extra: + generator: false + ntc_sponsor: true + social: + - icon: "fontawesome/solid/rss" + link: "https://blog.networktocode.com/" + name: "Network to Code Blog" + - icon: "fontawesome/brands/youtube" + link: "https://www.youtube.com/channel/UCwBh-dDdoqzxXKyvTw3BuTw" + name: "Network to Code Videos" + - icon: "fontawesome/brands/slack" + link: "https://www.networktocode.com/community/" + name: "Network to Code Community" + - icon: "fontawesome/brands/github" + link: "https://github.com/networktocode/" + name: "GitHub Organization" + - icon: "fontawesome/brands/twitter" + link: "https://twitter.com/networktocode" + name: "Network to Code Twitter" markdown_extensions: + - "markdown_version_annotations": + admonition_tag: "???" - "admonition" - - toc: + - "toc": permalink: true + - "attr_list" + - "markdown_data_tables": + base_path: "docs" + - "md_in_html" + - "pymdownx.details" + # Need pymdownx.emoji for Grid icon search + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - "pymdownx.highlight": + anchor_linenums: true + - "pymdownx.inlinehilite" + - "pymdownx.snippets" + - "pymdownx.superfences": + custom_fences: + - name: "mermaid" + class: "mermaid" + format: !!python/name:pymdownx.superfences.fence_code_format + - "pymdownx.tabbed": + "alternate_style": true + - "pymdownx.tilde" + +plugins: + - "search" + - "gen-files": + scripts: + - "docs/generate_code_reference_pages.py" + - "glightbox": + manual: true # See https://blueswen.github.io/mkdocs-glightbox/flexibility/enable-by-image-or-page/ + - "section-index" + - "mkdocstrings": + default_handler: "python" + handlers: + python: + paths: ["."] + options: + heading_level: 1 + show_root_heading: true + show_root_members_full_path: true + show_source: false + nav: - - Introduction: "index.md" - - Usage: "usage.md" - - Architecture: "architecture.md" -# Future docs -# - Examples: "example.md" -# - Development Environment: "development.md" + - Overview: "index.md" + - User Guide: + - Library Overview: "user/lib_overview.md" + - Getting Started: "user/lib_getting_started.md" + - Architecture Overview: "user/architecture.md" + - Usage: "user/usage.md" + - Frequently Asked Questions: "user/faq.md" + - Administrator Guide: + - Install and Configure: "admin/install.md" + - Upgrade: "admin/upgrade.md" + - Uninstall: "admin/uninstall.md" + - Release Notes: + - "admin/release_notes/index.md" + - v0.0: "admin/release_notes/version_0.0.md" + - v1.0: "admin/release_notes/version_1.0.md" + - Developer Guide: + - Extending the Library: "dev/extending.md" + - Contributing to the Library: "dev/contributing.md" + - Development Environment: "dev/dev_environment.md" + - Architecture Decisions: "dev/arch_decision.md" + - Code Reference: + - Jdiff: "code-reference/jdiff/__init__.md" + - check_types: "code-reference/jdiff/check_types.md" + - evaluators: "code-reference/jdiff/evaluators.md" + - extract_data: "code-reference/jdiff/extract_data.md" + - operator: "code-reference/jdiff/operator.md" + - jdiff_utils: "code-reference/jdiff/utils/__init__.md" + - data_normalization: "code-reference/jdiff/utils/data_normalization.md" + - diff_helpers: "code-reference/jdiff/utils/diff_helpers.md" + - jmespath_parsers: "code-reference/jdiff/utils/jmespath_parsers.md" diff --git a/poetry.lock b/poetry.lock index afa5da7..192066f 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,199 +1,181 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "astroid" -version = "3.2.4" +version = "3.3.11" description = "An abstract syntax tree for Python with inference support." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "astroid-3.2.4-py3-none-any.whl", hash = "sha256:413658a61eeca6202a59231abb473f932038fbcbf1666587f66d482083413a25"}, - {file = "astroid-3.2.4.tar.gz", hash = "sha256:0e14202810b30da1b735827f78f5157be2bbd4a7a59b7707ca0bfc2fb4c0063a"}, + {file = "astroid-3.3.11-py3-none-any.whl", hash = "sha256:54c760ae8322ece1abd213057c4b5bba7c49818853fc901ef09719a60dbf9dec"}, + {file = "astroid-3.3.11.tar.gz", hash = "sha256:1e5a5011af2920c7c67a53f65d536d65bfa7116feeaf2354d8b94f29573bb0ce"}, ] [package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=4", markers = "python_version < \"3.11\""} [[package]] -name = "bandit" -version = "1.7.10" -description = "Security oriented static analyser for python code." +name = "attrs" +version = "23.2.0" +description = "Classes Without Boilerplate" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "bandit-1.7.10-py3-none-any.whl", hash = "sha256:665721d7bebbb4485a339c55161ac0eedde27d51e638000d91c8c2d68343ad02"}, - {file = "bandit-1.7.10.tar.gz", hash = "sha256:59ed5caf5d92b6ada4bf65bc6437feea4a9da1093384445fed4d472acc6cff7b"}, + {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, + {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, ] -[package.dependencies] -colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} -PyYAML = ">=5.3.1" -rich = "*" -stevedore = ">=1.20.0" - [package.extras] -baseline = ["GitPython (>=3.1.30)"] -sarif = ["jschema-to-python (>=1.2.3)", "sarif-om (>=1.0.4)"] -test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)"] -toml = ["tomli (>=1.1.0)"] -yaml = ["PyYAML"] +cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] +dev = ["attrs[tests]", "pre-commit"] +docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] +tests = ["attrs[tests-no-zope]", "zope-interface"] +tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] +tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] [[package]] -name = "black" -version = "24.8.0" -description = "The uncompromising code formatter." +name = "babel" +version = "2.17.0" +description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ - {file = "black-24.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6"}, - {file = "black-24.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb"}, - {file = "black-24.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42"}, - {file = "black-24.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a"}, - {file = "black-24.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"}, - {file = "black-24.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af"}, - {file = "black-24.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4"}, - {file = "black-24.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af"}, - {file = "black-24.8.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368"}, - {file = "black-24.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed"}, - {file = "black-24.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018"}, - {file = "black-24.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2"}, - {file = "black-24.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd"}, - {file = "black-24.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2"}, - {file = "black-24.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e"}, - {file = "black-24.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920"}, - {file = "black-24.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c"}, - {file = "black-24.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e"}, - {file = "black-24.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47"}, - {file = "black-24.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb"}, - {file = "black-24.8.0-py3-none-any.whl", hash = "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed"}, - {file = "black-24.8.0.tar.gz", hash = "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f"}, + {file = "babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2"}, + {file = "babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d"}, ] -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} +[package.extras] +dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] + +[[package]] +name = "backrefs" +version = "5.9" +description = "A wrapper around re and regex that adds additional back references." +optional = false +python-versions = ">=3.9" +files = [ + {file = "backrefs-5.9-py310-none-any.whl", hash = "sha256:db8e8ba0e9de81fcd635f440deab5ae5f2591b54ac1ebe0550a2ca063488cd9f"}, + {file = "backrefs-5.9-py311-none-any.whl", hash = "sha256:6907635edebbe9b2dc3de3a2befff44d74f30a4562adbb8b36f21252ea19c5cf"}, + {file = "backrefs-5.9-py312-none-any.whl", hash = "sha256:7fdf9771f63e6028d7fee7e0c497c81abda597ea45d6b8f89e8ad76994f5befa"}, + {file = "backrefs-5.9-py313-none-any.whl", hash = "sha256:cc37b19fa219e93ff825ed1fed8879e47b4d89aa7a1884860e2db64ccd7c676b"}, + {file = "backrefs-5.9-py314-none-any.whl", hash = "sha256:df5e169836cc8acb5e440ebae9aad4bf9d15e226d3bad049cf3f6a5c20cc8dc9"}, + {file = "backrefs-5.9-py39-none-any.whl", hash = "sha256:f48ee18f6252b8f5777a22a00a09a85de0ca931658f1dd96d4406a34f3748c60"}, + {file = "backrefs-5.9.tar.gz", hash = "sha256:808548cb708d66b82ee231f962cb36faaf4f2baab032f2fbb783e9c2fdddaa59"}, +] [package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] +extras = ["regex"] [[package]] name = "certifi" -version = "2025.1.31" +version = "2025.8.3" description = "Python package for providing Mozilla's CA Bundle." optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "certifi-2025.8.3-py3-none-any.whl", hash = "sha256:f6c12493cfb1b06ba2ff328595af9350c65d6644968e5d3a2ffd78699af217a5"}, + {file = "certifi-2025.8.3.tar.gz", hash = "sha256:e564105f78ded564e3ae7c923924435e1daa7463faeab5bb932bc53ffae63407"}, ] [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] @@ -256,34 +238,21 @@ profile = ["gprof2dot (>=2022.7.29)"] [[package]] name = "exceptiongroup" -version = "1.2.2" +version = "1.3.0" description = "Backport of PEP 654 (exception groups)" optional = false python-versions = ">=3.7" files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, + {file = "exceptiongroup-1.3.0-py3-none-any.whl", hash = "sha256:4d111e6e0c13d0644cad6ddaa7ed0261a0b36971f6d23e7ec9b4b9097da78a10"}, + {file = "exceptiongroup-1.3.0.tar.gz", hash = "sha256:b241f5885f560bc56a59ee63ca4c6a8bfa46ae4ad651af316d4e81817bb9fd88"}, ] +[package.dependencies] +typing-extensions = {version = ">=4.6.0", markers = "python_version < \"3.13\""} + [package.extras] test = ["pytest (>=6)"] -[[package]] -name = "flake8" -version = "5.0.4" -description = "the modular source code checker: pep8 pyflakes and co" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, - {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, -] - -[package.dependencies] -mccabe = ">=0.7.0,<0.8.0" -pycodestyle = ">=2.9.0,<2.10.0" -pyflakes = ">=2.5.0,<2.6.0" - [[package]] name = "ghp-import" version = "2.1.0" @@ -301,6 +270,31 @@ python-dateutil = ">=2.8.1" [package.extras] dev = ["flake8", "markdown", "twine", "wheel"] +[[package]] +name = "griffe" +version = "1.1.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.1.1-py3-none-any.whl", hash = "sha256:0c469411e8d671a545725f5c0851a746da8bd99d354a79fdc4abd45219252efb"}, + {file = "griffe-1.1.1.tar.gz", hash = "sha256:faeb78764c0b2bd010719d6e015d07709b0f260258b5d4dd6c88343d9702aa30"}, +] + +[package.dependencies] +colorama = ">=0.4" + +[[package]] +name = "hjson" +version = "3.1.0" +description = "Hjson, a user interface for JSON." +optional = false +python-versions = "*" +files = [ + {file = "hjson-3.1.0-py3-none-any.whl", hash = "sha256:65713cdcf13214fb554eb8b4ef803419733f4f5e551047c9b711098ab7186b89"}, + {file = "hjson-3.1.0.tar.gz", hash = "sha256:55af475a27cf83a7969c808399d7bccdec8fb836a07ddbd574587593b9cdcf75"}, +] + [[package]] name = "idna" version = "3.10" @@ -317,22 +311,48 @@ all = ["flake8 (>=7.1.1)", "mypy (>=1.11.2)", "pytest (>=8.3.2)", "ruff (>=0.6.2 [[package]] name = "importlib-metadata" -version = "4.13.0" +version = "8.7.0" description = "Read metadata from Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" files = [ - {file = "importlib_metadata-4.13.0-py3-none-any.whl", hash = "sha256:8a8a81bcf996e74fee46f0d16bd3eaa382a7eb20fd82445c3ad11f4090334116"}, - {file = "importlib_metadata-4.13.0.tar.gz", hash = "sha256:dd0173e8f150d6815e098fd354f6414b0f079af4644ddfe90c71e2fc6174346d"}, + {file = "importlib_metadata-8.7.0-py3-none-any.whl", hash = "sha256:e5dd1551894c77868a30651cef00984d50e1002d06942a7101d34870c5f02afd"}, + {file = "importlib_metadata-8.7.0.tar.gz", hash = "sha256:d13b81ad223b890aa16c5471f2ac3056cf76c5f10f82d6f9292f0b415f389000"}, ] [package.dependencies] -zipp = ">=0.5" +zipp = ">=3.20" [package.extras] -docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] +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"] -testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] +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.5.2" +description = "Read resources from Python packages" +optional = false +python-versions = ">=3.9" +files = [ + {file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"}, + {file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"}, +] + +[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" @@ -358,17 +378,18 @@ files = [ [[package]] name = "isort" -version = "5.13.2" +version = "6.0.1" description = "A Python utility / library to sort Python imports." optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "isort-5.13.2-py3-none-any.whl", hash = "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6"}, - {file = "isort-5.13.2.tar.gz", hash = "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109"}, + {file = "isort-6.0.1-py3-none-any.whl", hash = "sha256:2dc5d7f65c9678d94c88dfc29161a320eec67328bc97aad576874cb4be1e9615"}, + {file = "isort-6.0.1.tar.gz", hash = "sha256:1cb5df28dfbc742e490c5e41bad6da41b805b0a8be7bc93cd0fb2a8a890ac450"}, ] [package.extras] -colors = ["colorama (>=0.4.6)"] +colors = ["colorama"] +plugins = ["setuptools"] [[package]] name = "jinja2" @@ -400,113 +421,120 @@ files = [ [[package]] name = "markdown" -version = "3.7" +version = "3.8.2" description = "Python implementation of John Gruber's Markdown." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "Markdown-3.7-py3-none-any.whl", hash = "sha256:7eb6df5690b81a1d7942992c97fad2938e956e79df20cbc6186e9c3a77b1c803"}, - {file = "markdown-3.7.tar.gz", hash = "sha256:2ae2471477cfd02dbbf038d5d9bc226d40def84b4fe2986e49b59b6b472bbed2"}, + {file = "markdown-3.8.2-py3-none-any.whl", hash = "sha256:5c83764dbd4e00bdd94d85a19b8d55ccca20fe35b2e678a1422b380324dd5f24"}, + {file = "markdown-3.8.2.tar.gz", hash = "sha256:247b9a70dd12e27f67431ce62523e675b866d254f900c4fe75ce3dda62237c45"}, ] [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]"] +docs = ["mdx_gh_links (>=0.2)", "mkdocs (>=1.6)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] testing = ["coverage", "pyyaml"] [[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" +name = "markdown-data-tables" +version = "1.0.0" +description = "Embed data files such as YAML as tables in a Markdown document" optional = false -python-versions = ">=3.8" +python-versions = ">=3.8,<4.0" files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, + {file = "markdown_data_tables-1.0.0-py3-none-any.whl", hash = "sha256:a59c6743685691ced4341bdb01024b7a863a1adaa3a2ef92fa068a7e90227d9a"}, + {file = "markdown_data_tables-1.0.0.tar.gz", hash = "sha256:ac1b07c58bb66e9f060ba81cdd63070ec94deb21f0147e519c77c8475ba696ea"}, ] [package.dependencies] -mdurl = ">=0.1,<1.0" +markdown = ">=3.3.7,<4.0.0" +pyyaml = ">=6.0,<7.0" +tabulate = ">=0.9.0,<0.10.0" -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] +[[package]] +name = "markdown-version-annotations" +version = "1.0.1" +description = "Markdown plugin to add custom admonitions for documenting version differences" +optional = false +python-versions = "<4.0,>=3.7" +files = [ + {file = "markdown_version_annotations-1.0.1-py3-none-any.whl", hash = "sha256:6df0b2ac08bab906c8baa425f59fc0fe342fbe8b3917c144fb75914266b33200"}, + {file = "markdown_version_annotations-1.0.1.tar.gz", hash = "sha256:620aade507ef175ccfb2059db152a34c6a1d2add28c2be16ea4de38d742e6132"}, +] + +[package.dependencies] +markdown = ">=3.3.7,<4.0.0" [[package]] name = "markupsafe" -version = "2.1.5" +version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" 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"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win32.whl", hash = "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50"}, + {file = "MarkupSafe-3.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d"}, + {file = "MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30"}, + {file = "MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1"}, + {file = "MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6"}, + {file = "MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win32.whl", hash = "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f"}, + {file = "MarkupSafe-3.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a"}, + {file = "markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0"}, ] [[package]] @@ -520,17 +548,6 @@ files = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - [[package]] name = "mergedeep" version = "1.3.4" @@ -573,6 +590,36 @@ watchdog = ">=2.0" 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.4.2" +description = "Automatically link across pages in MkDocs." +optional = false +python-versions = ">=3.9" +files = [ + {file = "mkdocs_autorefs-1.4.2-py3-none-any.whl", hash = "sha256:83d6d777b66ec3c372a1aad4ae0cf77c243ba5bcda5bf0c6b8a2c5e7a3d89f13"}, + {file = "mkdocs_autorefs-1.4.2.tar.gz", hash = "sha256:e2ebe1abd2b67d597ed19378c0fff84d73d1dbce411fce7a7cc6f161888b6749"}, +] + +[package.dependencies] +Markdown = ">=3.3" +markupsafe = ">=2.0.1" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-gen-files" +version = "0.5.0" +description = "MkDocs plugin to programmatically generate documentation pages during the build" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mkdocs_gen_files-0.5.0-py3-none-any.whl", hash = "sha256:7ac060096f3f40bd19039e7277dd3050be9a453c8ac578645844d4d91d7978ea"}, + {file = "mkdocs_gen_files-0.5.0.tar.gz", hash = "sha256:4c7cf256b5d67062a788f6b1d035e157fc1a9498c2399be9af5257d4ff4d19bc"}, +] + +[package.dependencies] +mkdocs = ">=1.0.3" + [[package]] name = "mkdocs-get-deps" version = "0.2.0" @@ -591,219 +638,260 @@ platformdirs = ">=2.2.0" pyyaml = ">=5.1" [[package]] -name = "mkdocs-include-markdown-plugin" -version = "3.9.1" -description = "Mkdocs Markdown includer plugin." +name = "mkdocs-glightbox" +version = "0.4.0" +description = "MkDocs plugin supports image lightbox with GLightbox." +optional = false +python-versions = "*" +files = [ + {file = "mkdocs-glightbox-0.4.0.tar.gz", hash = "sha256:392b34207bf95991071a16d5f8916d1d2f2cd5d5bb59ae2997485ccd778c70d9"}, + {file = "mkdocs_glightbox-0.4.0-py3-none-any.whl", hash = "sha256:e0107beee75d3eb7380ac06ea2d6eac94c999eaa49f8c3cbab0e7be2ac006ccf"}, +] + +[[package]] +name = "mkdocs-macros-plugin" +version = "1.3.7" +description = "Unleash the power of MkDocs with macros and variables" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "mkdocs_include_markdown_plugin-3.9.1-py3-none-any.whl", hash = "sha256:f33687e29ac66d045ba181ea50f054646b0090b42b0a4318f08e7f1d1235e6f6"}, - {file = "mkdocs_include_markdown_plugin-3.9.1.tar.gz", hash = "sha256:5e5698e78d7fea111be9873a456089daa333497988405acaac8eba2924a19152"}, + {file = "mkdocs_macros_plugin-1.3.7-py3-none-any.whl", hash = "sha256:02432033a5b77fb247d6ec7924e72fc4ceec264165b1644ab8d0dc159c22ce59"}, + {file = "mkdocs_macros_plugin-1.3.7.tar.gz", hash = "sha256:17c7fd1a49b94defcdb502fd453d17a1e730f8836523379d21292eb2be4cb523"}, ] +[package.dependencies] +hjson = "*" +jinja2 = "*" +mkdocs = ">=0.17" +packaging = "*" +pathspec = "*" +python-dateutil = "*" +pyyaml = "*" +super-collections = "*" +termcolor = "*" + [package.extras] -dev = ["bump2version (==1.0.1)", "mkdocs (==1.4.0)", "pre-commit", "pytest (==7.1.3)", "pytest-cov (==3.0.0)", "tox"] -test = ["mkdocs (==1.4.0)", "pytest (==7.1.3)", "pytest-cov (==3.0.0)"] +test = ["mkdocs-d2-plugin", "mkdocs-include-markdown-plugin", "mkdocs-macros-test", "mkdocs-material (>=6.2)", "mkdocs-test"] [[package]] -name = "mypy" -version = "1.14.1" -description = "Optional static typing for Python" +name = "mkdocs-material" +version = "9.6.15" +description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mypy-1.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:52686e37cf13d559f668aa398dd7ddf1f92c5d613e4f8cb262be2fb4fedb0fcb"}, - {file = "mypy-1.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1fb545ca340537d4b45d3eecdb3def05e913299ca72c290326be19b3804b39c0"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:90716d8b2d1f4cd503309788e51366f07c56635a3309b0f6a32547eaaa36a64d"}, - {file = "mypy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2ae753f5c9fef278bcf12e1a564351764f2a6da579d4a81347e1d5a15819997b"}, - {file = "mypy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:e0fe0f5feaafcb04505bcf439e991c6d8f1bf8b15f12b05feeed96e9e7bf1427"}, - {file = "mypy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:7d54bd85b925e501c555a3227f3ec0cfc54ee8b6930bd6141ec872d1c572f81f"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f995e511de847791c3b11ed90084a7a0aafdc074ab88c5a9711622fe4751138c"}, - {file = "mypy-1.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d64169ec3b8461311f8ce2fd2eb5d33e2d0f2c7b49116259c51d0d96edee48d1"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba24549de7b89b6381b91fbc068d798192b1b5201987070319889e93038967a8"}, - {file = "mypy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:183cf0a45457d28ff9d758730cd0210419ac27d4d3f285beda038c9083363b1f"}, - {file = "mypy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f2a0ecc86378f45347f586e4163d1769dd81c5a223d577fe351f26b179e148b1"}, - {file = "mypy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:ad3301ebebec9e8ee7135d8e3109ca76c23752bac1e717bc84cd3836b4bf3eae"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:30ff5ef8519bbc2e18b3b54521ec319513a26f1bba19a7582e7b1f58a6e69f14"}, - {file = "mypy-1.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb9f255c18052343c70234907e2e532bc7e55a62565d64536dbc7706a20b78b9"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8b4e3413e0bddea671012b063e27591b953d653209e7a4fa5e48759cda77ca11"}, - {file = "mypy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:553c293b1fbdebb6c3c4030589dab9fafb6dfa768995a453d8a5d3b23784af2e"}, - {file = "mypy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fad79bfe3b65fe6a1efaed97b445c3d37f7be9fdc348bdb2d7cac75579607c89"}, - {file = "mypy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fa2220e54d2946e94ab6dbb3ba0a992795bd68b16dc852db33028df2b00191b"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:92c3ed5afb06c3a8e188cb5da4984cab9ec9a77ba956ee419c68a388b4595255"}, - {file = "mypy-1.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dbec574648b3e25f43d23577309b16534431db4ddc09fda50841f1e34e64ed34"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8c6d94b16d62eb3e947281aa7347d78236688e21081f11de976376cf010eb31a"}, - {file = "mypy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d4b19b03fdf54f3c5b2fa474c56b4c13c9dbfb9a2db4370ede7ec11a2c5927d9"}, - {file = "mypy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c911fde686394753fff899c409fd4e16e9b294c24bfd5e1ea4675deae1ac6fd"}, - {file = "mypy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:8b21525cb51671219f5307be85f7e646a153e5acc656e5cebf64bfa076c50107"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7084fb8f1128c76cd9cf68fe5971b37072598e7c31b2f9f95586b65c741a9d31"}, - {file = "mypy-1.14.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f845a00b4f420f693f870eaee5f3e2692fa84cc8514496114649cfa8fd5e2c6"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:44bf464499f0e3a2d14d58b54674dee25c031703b2ffc35064bd0df2e0fac319"}, - {file = "mypy-1.14.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c99f27732c0b7dc847adb21c9d47ce57eb48fa33a17bc6d7d5c5e9f9e7ae5bac"}, - {file = "mypy-1.14.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:bce23c7377b43602baa0bd22ea3265c49b9ff0b76eb315d6c34721af4cdf1d9b"}, - {file = "mypy-1.14.1-cp38-cp38-win_amd64.whl", hash = "sha256:8edc07eeade7ebc771ff9cf6b211b9a7d93687ff892150cb5692e4f4272b0837"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3888a1816d69f7ab92092f785a462944b3ca16d7c470d564165fe703b0970c35"}, - {file = "mypy-1.14.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:46c756a444117c43ee984bd055db99e498bc613a70bbbc120272bd13ca579fbc"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:27fc248022907e72abfd8e22ab1f10e903915ff69961174784a3900a8cba9ad9"}, - {file = "mypy-1.14.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:499d6a72fb7e5de92218db961f1a66d5f11783f9ae549d214617edab5d4dbdbb"}, - {file = "mypy-1.14.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57961db9795eb566dc1d1b4e9139ebc4c6b0cb6e7254ecde69d1552bf7613f60"}, - {file = "mypy-1.14.1-cp39-cp39-win_amd64.whl", hash = "sha256:07ba89fdcc9451f2ebb02853deb6aaaa3d2239a236669a63ab3801bbf923ef5c"}, - {file = "mypy-1.14.1-py3-none-any.whl", hash = "sha256:b66a60cc4073aeb8ae00057f9c1f64d49e90f918fbcef9a977eb121da8b8f1d1"}, - {file = "mypy-1.14.1.tar.gz", hash = "sha256:7ec88144fe9b510e8475ec2f5f251992690fcf89ccb4500b214b4226abcd32d6"}, + {file = "mkdocs_material-9.6.15-py3-none-any.whl", hash = "sha256:ac969c94d4fe5eb7c924b6d2f43d7db41159ea91553d18a9afc4780c34f2717a"}, + {file = "mkdocs_material-9.6.15.tar.gz", hash = "sha256:64adf8fa8dba1a17905b6aee1894a5aafd966d4aeb44a11088519b0f5ca4f1b5"}, ] [package.dependencies] -mypy_extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing_extensions = ">=4.6.0" +babel = ">=2.10,<3.0" +backrefs = ">=5.7.post1,<6.0" +colorama = ">=0.4,<1.0" +jinja2 = ">=3.1,<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" +requests = ">=2.26,<3.0" [package.extras] -dmypy = ["psutil (>=4.0)"] -faster-cache = ["orjson"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] +git = ["mkdocs-git-committers-plugin-2 (>=1.1,<3)", "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 = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." +name = "mkdocs-material-extensions" +version = "1.3.1" +description = "Extension pack for Python Markdown and MkDocs Material." optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, + {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 = "ordered-set" -version = "4.1.0" -description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" +name = "mkdocs-redirects" +version = "1.2.2" +description = "A MkDocs plugin for dynamic page redirects to prevent broken links" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, - {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, + {file = "mkdocs_redirects-1.2.2-py3-none-any.whl", hash = "sha256:7dbfa5647b79a3589da4401403d69494bd1f4ad03b9c15136720367e1f340ed5"}, + {file = "mkdocs_redirects-1.2.2.tar.gz", hash = "sha256:3094981b42ffab29313c2c1b8ac3969861109f58b2dd58c45fc81cd44bfa0095"}, ] -[package.extras] -dev = ["black", "mypy", "pytest"] +[package.dependencies] +mkdocs = ">=1.1.1" [[package]] -name = "packaging" -version = "24.2" -description = "Core utilities for Python packages" +name = "mkdocs-section-index" +version = "0.3.10" +description = "MkDocs plugin to allow clickable sections that lead to an index page" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "mkdocs_section_index-0.3.10-py3-none-any.whl", hash = "sha256:bc27c0d0dc497c0ebaee1fc72839362aed77be7318b5ec0c30628f65918e4776"}, + {file = "mkdocs_section_index-0.3.10.tar.gz", hash = "sha256:a82afbda633c82c5568f0e3b008176b9b365bf4bd8b6f919d6eff09ee146b9f8"}, ] +[package.dependencies] +mkdocs = ">=1.2" + [[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." +name = "mkdocstrings" +version = "0.27.0" +description = "Automatic documentation from sources, for MkDocs." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, + {file = "mkdocstrings-0.27.0-py3-none-any.whl", hash = "sha256:6ceaa7ea830770959b55a16203ac63da24badd71325b96af950e59fd37366332"}, + {file = "mkdocstrings-0.27.0.tar.gz", hash = "sha256:16adca6d6b0a1f9e0c07ff0b02ced8e16f228a9d65a37c063ec4c14d7b76a657"}, ] +[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" +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 = "pbr" -version = "6.1.1" -description = "Python Build Reasonableness" +name = "mkdocstrings-python" +version = "1.13.0" +description = "A Python handler for mkdocstrings." optional = false -python-versions = ">=2.6" +python-versions = ">=3.9" files = [ - {file = "pbr-6.1.1-py2.py3-none-any.whl", hash = "sha256:38d4daea5d9fa63b3f626131b9d34947fd0c8be9b05a29276870580050a25a76"}, - {file = "pbr-6.1.1.tar.gz", hash = "sha256:93ea72ce6989eb2eed99d0f75721474f69ad88128afdef5ac377eb797c4bf76b"}, + {file = "mkdocstrings_python-1.13.0-py3-none-any.whl", hash = "sha256:b88bbb207bab4086434743849f8e796788b373bd32e7bfefbf8560ac45d88f97"}, + {file = "mkdocstrings_python-1.13.0.tar.gz", hash = "sha256:2dbd5757e8375b9720e81db16f52f1856bf59905428fd7ef88005d1370e2f64c"}, ] [package.dependencies] -setuptools = "*" +griffe = ">=0.49" +mkdocs-autorefs = ">=1.2" +mkdocstrings = ">=0.26" [[package]] -name = "platformdirs" -version = "4.3.6" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +name = "mock" +version = "5.2.0" +description = "Rolling backport of unittest.mock for all Pythons" optional = false -python-versions = ">=3.8" +python-versions = ">=3.6" files = [ - {file = "platformdirs-4.3.6-py3-none-any.whl", hash = "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb"}, - {file = "platformdirs-4.3.6.tar.gz", hash = "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907"}, + {file = "mock-5.2.0-py3-none-any.whl", hash = "sha256:7ba87f72ca0e915175596069dbbcc7c75af7b5e9b9bc107ad6349ede0819982f"}, + {file = "mock-5.2.0.tar.gz", hash = "sha256:4e460e818629b4b173f32d08bf30d3af8123afbb8e04bb5707a1fd4799e503f0"}, ] [package.extras] -docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.0.2)", "sphinx-autodoc-typehints (>=2.4)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.2)", "pytest-cov (>=5)", "pytest-mock (>=3.14)"] -type = ["mypy (>=1.11.2)"] +build = ["blurb", "twine", "wheel"] +docs = ["sphinx"] +test = ["pytest", "pytest-cov"] [[package]] -name = "pluggy" -version = "1.5.0" -description = "plugin and hook calling mechanisms for python" +name = "ordered-set" +version = "4.1.0" +description = "An OrderedSet is a custom MutableSet that remembers its order, so that every" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, - {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, + {file = "ordered-set-4.1.0.tar.gz", hash = "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8"}, + {file = "ordered_set-4.1.0-py3-none-any.whl", hash = "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562"}, ] [package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] +dev = ["black", "mypy", "pytest"] [[package]] -name = "pycodestyle" -version = "2.9.1" -description = "Python style guide checker" +name = "packaging" +version = "25.0" +description = "Core utilities for Python packages" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, - {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] -name = "pydocstyle" -version = "6.3.0" -description = "Python docstring style checker" +name = "paginate" +version = "0.5.7" +description = "Divides large result sets into pages for easier browsing" optional = false -python-versions = ">=3.6" +python-versions = "*" files = [ - {file = "pydocstyle-6.3.0-py3-none-any.whl", hash = "sha256:118762d452a49d6b05e194ef344a55822987a462831ade91ec5c06fd2169d019"}, - {file = "pydocstyle-6.3.0.tar.gz", hash = "sha256:7ce43f0c0ac87b07494eb9c0b462c0b73e6ff276807f204d6b53edc72b7e44e1"}, + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, ] -[package.dependencies] -snowballstemmer = ">=2.2.0" +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + +[[package]] +name = "pathspec" +version = "0.12.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.8" +files = [ + {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, + {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, +] + +[[package]] +name = "platformdirs" +version = "4.3.8" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." +optional = false +python-versions = ">=3.9" +files = [ + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, +] [package.extras] -toml = ["tomli (>=1.2.3)"] +docs = ["furo (>=2024.8.6)", "proselint (>=0.14)", "sphinx (>=8.1.3)", "sphinx-autodoc-typehints (>=3)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=8.3.4)", "pytest-cov (>=6)", "pytest-mock (>=3.14)"] +type = ["mypy (>=1.14.1)"] [[package]] -name = "pyflakes" -version = "2.5.0" -description = "passive checker of Python programs" +name = "pluggy" +version = "1.6.0" +description = "plugin and hook calling mechanisms for python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" files = [ - {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, - {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, + {file = "pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746"}, + {file = "pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3"}, ] +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["coverage", "pytest", "pytest-benchmark"] + [[package]] name = "pygments" -version = "2.19.1" +version = "2.19.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.8" files = [ - {file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"}, - {file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"}, + {file = "pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b"}, + {file = "pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887"}, ] [package.extras] @@ -811,55 +899,74 @@ windows-terminal = ["colorama (>=0.4.6)"] [[package]] name = "pylint" -version = "3.2.7" +version = "3.3.7" description = "python code static checker" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.9.0" files = [ - {file = "pylint-3.2.7-py3-none-any.whl", hash = "sha256:02f4aedeac91be69fb3b4bea997ce580a4ac68ce58b89eaefeaf06749df73f4b"}, - {file = "pylint-3.2.7.tar.gz", hash = "sha256:1b7a721b575eaeaa7d39db076b6e7743c993ea44f57979127c517c6c572c803e"}, + {file = "pylint-3.3.7-py3-none-any.whl", hash = "sha256:43860aafefce92fca4cf6b61fe199cdc5ae54ea28f9bf4cd49de267b5195803d"}, + {file = "pylint-3.3.7.tar.gz", hash = "sha256:2b11de8bde49f9c5059452e0c310c079c746a0a8eeaa789e5aa966ecc23e4559"}, ] [package.dependencies] -astroid = ">=3.2.4,<=3.3.0-dev0" +astroid = ">=3.3.8,<=3.4.0.dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ {version = ">=0.2", markers = "python_version < \"3.11\""}, {version = ">=0.3.7", markers = "python_version >= \"3.12\""}, {version = ">=0.3.6", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, ] -isort = ">=4.2.5,<5.13.0 || >5.13.0,<6" +isort = ">=4.2.5,<5.13 || >5.13,<7" mccabe = ">=0.6,<0.8" -platformdirs = ">=2.2.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +platformdirs = ">=2.2" +tomli = {version = ">=1.1", markers = "python_version < \"3.11\""} tomlkit = ">=0.10.1" -typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} [package.extras] spelling = ["pyenchant (>=3.2,<4.0)"] testutils = ["gitpython (>3)"] +[[package]] +name = "pymdown-extensions" +version = "10.16.1" +description = "Extension pack for Python Markdown." +optional = false +python-versions = ">=3.9" +files = [ + {file = "pymdown_extensions-10.16.1-py3-none-any.whl", hash = "sha256:d6ba157a6c03146a7fb122b2b9a121300056384eafeec9c9f9e584adfdb2a32d"}, + {file = "pymdown_extensions-10.16.1.tar.gz", hash = "sha256:aace82bcccba3efc03e25d584e6a22d27a8e17caa3f4dd9f207e49b787aa9a91"}, +] + +[package.dependencies] +markdown = ">=3.6" +pyyaml = "*" + +[package.extras] +extra = ["pygments (>=2.19.1)"] + [[package]] name = "pytest" -version = "8.3.5" +version = "8.4.1" description = "pytest: simple powerful testing with Python" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820"}, - {file = "pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845"}, + {file = "pytest-8.4.1-py3-none-any.whl", hash = "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7"}, + {file = "pytest-8.4.1.tar.gz", hash = "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c"}, ] [package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" +colorama = {version = ">=0.4", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1", markers = "python_version < \"3.11\""} +iniconfig = ">=1" +packaging = ">=20" pluggy = ">=1.5,<2" +pygments = ">=2.7.2" tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] -dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "requests", "setuptools", "xmlschema"] [[package]] name = "python-dateutil" @@ -939,13 +1046,13 @@ files = [ [[package]] name = "pyyaml-env-tag" -version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " +version = "1.1" +description = "A custom YAML tag for referencing environment variables in YAML files." optional = false -python-versions = ">=3.6" +python-versions = ">=3.9" 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"}, + {file = "pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04"}, + {file = "pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff"}, ] [package.dependencies] @@ -953,18 +1060,18 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.32.3" +version = "2.32.4" 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"}, + {file = "requests-2.32.4-py3-none-any.whl", hash = "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c"}, + {file = "requests-2.32.4.tar.gz", hash = "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" +charset_normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<3" @@ -990,44 +1097,32 @@ requests = ">=2.22,<3" fixture = ["fixtures"] [[package]] -name = "rich" -version = "14.0.0" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +name = "ruff" +version = "0.12.8" +description = "An extremely fast Python linter and code formatter, written in Rust." optional = false -python-versions = ">=3.8.0" -files = [ - {file = "rich-14.0.0-py3-none-any.whl", hash = "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0"}, - {file = "rich-14.0.0.tar.gz", hash = "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" -typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.11\""} - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "setuptools" -version = "75.3.2" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "setuptools-75.3.2-py3-none-any.whl", hash = "sha256:90ab613b6583fc02d5369cbca13ea26ea0e182d1df2d943ee9cbe81d4c61add9"}, - {file = "setuptools-75.3.2.tar.gz", hash = "sha256:3c1383e1038b68556a382c1e8ded8887cd20141b0eb5708a6c8d277de49364f5"}, + {file = "ruff-0.12.8-py3-none-linux_armv6l.whl", hash = "sha256:63cb5a5e933fc913e5823a0dfdc3c99add73f52d139d6cd5cc8639d0e0465513"}, + {file = "ruff-0.12.8-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9a9bbe28f9f551accf84a24c366c1aa8774d6748438b47174f8e8565ab9dedbc"}, + {file = "ruff-0.12.8-py3-none-macosx_11_0_arm64.whl", hash = "sha256:2fae54e752a3150f7ee0e09bce2e133caf10ce9d971510a9b925392dc98d2fec"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0acbcf01206df963d9331b5838fb31f3b44fa979ee7fa368b9b9057d89f4a53"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ae3e7504666ad4c62f9ac8eedb52a93f9ebdeb34742b8b71cd3cccd24912719f"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cb82efb5d35d07497813a1c5647867390a7d83304562607f3579602fa3d7d46f"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:dbea798fc0065ad0b84a2947b0aff4233f0cb30f226f00a2c5850ca4393de609"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49ebcaccc2bdad86fd51b7864e3d808aad404aab8df33d469b6e65584656263a"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ac9c570634b98c71c88cb17badd90f13fc076a472ba6ef1d113d8ed3df109fb"}, + {file = "ruff-0.12.8-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:560e0cd641e45591a3e42cb50ef61ce07162b9c233786663fdce2d8557d99818"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:71c83121512e7743fba5a8848c261dcc454cafb3ef2934a43f1b7a4eb5a447ea"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:de4429ef2ba091ecddedd300f4c3f24bca875d3d8b23340728c3cb0da81072c3"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a2cab5f60d5b65b50fba39a8950c8746df1627d54ba1197f970763917184b161"}, + {file = "ruff-0.12.8-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:45c32487e14f60b88aad6be9fd5da5093dbefb0e3e1224131cb1d441d7cb7d46"}, + {file = "ruff-0.12.8-py3-none-win32.whl", hash = "sha256:daf3475060a617fd5bc80638aeaf2f5937f10af3ec44464e280a9d2218e720d3"}, + {file = "ruff-0.12.8-py3-none-win_amd64.whl", hash = "sha256:7209531f1a1fcfbe8e46bcd7ab30e2f43604d8ba1c49029bb420b103d0b5f76e"}, + {file = "ruff-0.12.8-py3-none-win_arm64.whl", hash = "sha256:c90e1a334683ce41b0e7a04f41790c429bf5073b62c1ae701c9dc5b3d14f0749"}, + {file = "ruff-0.12.8.tar.gz", hash = "sha256:4cb3a45525176e1009b2b64126acf5f9444ea59066262791febf55e40493a033"}, ] -[package.extras] -check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.5.2)"] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.collections", "jaraco.functools", "jaraco.text (>=3.7)", "more-itertools", "more-itertools (>=8.8)", "packaging", "packaging (>=24)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -cover = ["pytest-cov"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -enabler = ["pytest-enabler (>=2.2)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "ruff (<=0.7.1)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] -type = ["importlib-metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.12.*)", "pytest-mypy"] - [[package]] name = "six" version = "1.17.0" @@ -1040,29 +1135,49 @@ files = [ ] [[package]] -name = "snowballstemmer" -version = "2.2.0" -description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." +name = "super-collections" +version = "0.5.3" +description = "file: README.md" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, - {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, + {file = "super_collections-0.5.3-py3-none-any.whl", hash = "sha256:907d35b25dc4070910e8254bf2f5c928348af1cf8a1f1e8259e06c666e902cff"}, + {file = "super_collections-0.5.3.tar.gz", hash = "sha256:94c1ec96c0a0d5e8e7d389ed8cde6882ac246940507c5e6b86e91945c2968d46"}, ] +[package.dependencies] +hjson = "*" + +[package.extras] +test = ["pytest (>=7.0)"] + [[package]] -name = "stevedore" -version = "5.3.0" -description = "Manage dynamic plugins for Python applications" +name = "tabulate" +version = "0.9.0" +description = "Pretty-print tabular data" optional = false -python-versions = ">=3.8" +python-versions = ">=3.7" files = [ - {file = "stevedore-5.3.0-py3-none-any.whl", hash = "sha256:1efd34ca08f474dad08d9b19e934a22c68bb6fe416926479ba29e5013bcc8f78"}, - {file = "stevedore-5.3.0.tar.gz", hash = "sha256:9a64265f4060312828151c204efbe9b7a9852a0d9228756344dbc7e4023e375a"}, + {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, + {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, ] -[package.dependencies] -pbr = ">=2.0.0" +[package.extras] +widechars = ["wcwidth"] + +[[package]] +name = "termcolor" +version = "3.1.0" +description = "ANSI color formatting for output in terminal" +optional = false +python-versions = ">=3.9" +files = [ + {file = "termcolor-3.1.0-py3-none-any.whl", hash = "sha256:591dd26b5c2ce03b9e43f391264626557873ce1d379019786f99b0c2bee140aa"}, + {file = "termcolor-3.1.0.tar.gz", hash = "sha256:6a6dd7fbee581909eeec6a756cff1d7f7c376063b14e4a298dc4980309e55970"}, +] + +[package.extras] +tests = ["pytest", "pytest-cov"] [[package]] name = "toml" @@ -1118,35 +1233,56 @@ files = [ [[package]] name = "tomlkit" -version = "0.13.2" +version = "0.13.3" description = "Style preserving TOML library" optional = false python-versions = ">=3.8" files = [ - {file = "tomlkit-0.13.2-py3-none-any.whl", hash = "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde"}, - {file = "tomlkit-0.13.2.tar.gz", hash = "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79"}, + {file = "tomlkit-0.13.3-py3-none-any.whl", hash = "sha256:c89c649d79ee40629a9fda55f8ace8c6a1b42deb912b2a8fd8d942ddadb606b0"}, + {file = "tomlkit-0.13.3.tar.gz", hash = "sha256:430cf247ee57df2b94ee3fbe588e71d362a941ebb545dec29b53961d61add2a1"}, ] [[package]] -name = "typing-extensions" -version = "4.13.2" -description = "Backported and Experimental Type Hints for Python 3.8+" +name = "towncrier" +version = "24.8.0" +description = "Building newsfiles for your project." optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, - {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, + {file = "towncrier-24.8.0-py3-none-any.whl", hash = "sha256:9343209592b839209cdf28c339ba45792fbfe9775b5f9c177462fd693e127d8d"}, + {file = "towncrier-24.8.0.tar.gz", hash = "sha256:013423ee7eed102b2f393c287d22d95f66f1a3ea10a4baa82d298001a7f18af3"}, +] + +[package.dependencies] +click = "*" +importlib-metadata = {version = ">=4.6", markers = "python_version < \"3.10\""} +importlib-resources = {version = ">=5", markers = "python_version < \"3.10\""} +jinja2 = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +dev = ["furo (>=2024.05.06)", "nox", "packaging", "sphinx (>=5)", "twisted"] + +[[package]] +name = "typing-extensions" +version = "4.14.1" +description = "Backported and Experimental Type Hints for Python 3.9+" +optional = false +python-versions = ">=3.9" +files = [ + {file = "typing_extensions-4.14.1-py3-none-any.whl", hash = "sha256:d1e1e3b58374dc93031d6eda2420a48ea44a36c2b4766a4fdeb3710755731d76"}, + {file = "typing_extensions-4.14.1.tar.gz", hash = "sha256:38b39f4aeeab64884ce9f74c94263ef78f3c22467c8724005483154c26648d36"}, ] [[package]] name = "urllib3" -version = "2.2.3" +version = "2.5.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "urllib3-2.2.3-py3-none-any.whl", hash = "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac"}, - {file = "urllib3-2.2.3.tar.gz", hash = "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9"}, + {file = "urllib3-2.5.0-py3-none-any.whl", hash = "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc"}, + {file = "urllib3-2.5.0.tar.gz", hash = "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760"}, ] [package.extras] @@ -1157,46 +1293,41 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "watchdog" -version = "4.0.2" +version = "6.0.0" description = "Filesystem events monitoring" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" 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"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112"}, + {file = "watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2"}, + {file = "watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860"}, + {file = "watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134"}, + {file = "watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a"}, + {file = "watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881"}, + {file = "watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa"}, + {file = "watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c"}, + {file = "watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2"}, + {file = "watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a"}, + {file = "watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680"}, + {file = "watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f"}, + {file = "watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282"}, ] [package.extras] @@ -1204,13 +1335,13 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "yamllint" -version = "1.35.1" +version = "1.37.1" description = "A linter for YAML files." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"}, - {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"}, + {file = "yamllint-1.37.1-py3-none-any.whl", hash = "sha256:364f0d79e81409f591e323725e6a9f4504c8699ddf2d7263d8d2b539cd66a583"}, + {file = "yamllint-1.37.1.tar.gz", hash = "sha256:81f7c0c5559becc8049470d86046b36e96113637bcbe4753ecef06977c00245d"}, ] [package.dependencies] @@ -1222,13 +1353,13 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [[package]] name = "zipp" -version = "3.20.2" +version = "3.23.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "zipp-3.20.2-py3-none-any.whl", hash = "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350"}, - {file = "zipp-3.20.2.tar.gz", hash = "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29"}, + {file = "zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e"}, + {file = "zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166"}, ] [package.extras] @@ -1236,10 +1367,10 @@ 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"] +test = ["big-O", "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" -content-hash = "c79cbf961c23d5d5f5a384d05234fd253a054864f7141e150bf64fad9d66a7fe" +python-versions = "^3.9" +content-hash = "c8b46362dcca35882eae9af18704f8b4ba03b845afca546756d1c42729ab4d74" diff --git a/pyproject.toml b/pyproject.toml index ed2ab19..c8736f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,85 +1,126 @@ [tool.poetry] name = "jdiff" -version = "0.0.7" +version = "1.0.0" description = "A light-weight library to compare structured output from network devices show commands." authors = ["Network to Code, LLC "] -license = "Apache-2.0" -homepage = "https://github.com/networktocode/jdiff" -repository = "https://github.com/networktocode/jdiff" readme = "README.md" +license = "Apache-2.0" keywords = ["json", "diff", "network"] classifiers = [ + "Intended Audience :: Developers", + "Development Status :: 5 - Production/Stable", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", ] +homepage = "https://jdiff.readthedocs.io" +repository = "https://github.com/networktocode/jdiff" +documentation = "https://jdiff.readthedocs.io" + include = [ - "CHANGELOG.md", - "CONTRIBUTING.md", "LICENSE", + "README.md", ] [tool.poetry.dependencies] -python = "^3.8" +python = "^3.9" deepdiff = ">=5.5.0 <7.0" jmespath = "^1.0.1" -[tool.poetry.dev-dependencies] -pytest = "*" +[tool.poetry.group.dev.dependencies] requests_mock = "*" -pyyaml = "*" -black = "*" -pylint = "*" -pydocstyle = "*" -yamllint = "*" -bandit = "*" -invoke = "*" -toml = "*" -flake8 = "*" -mypy = "*" -mkdocs = "^1.3.1" -mkdocs-include-markdown-plugin = "^3.6.1" -importlib-metadata = "<5.0.0" # https://importlib-metadata.readthedocs.io/en/latest/history.html#v5-0-0 - -[tool.black] +pytest = "*" +mock = "*" +griffe = "1.1.1" +pyyaml = "^6.0.1" +pylint = "^3.1.0" +yamllint = "^1.35.1" +invoke = "^2.2.0" +toml = "^0.10.2" +attrs = "^23.2.0" +towncrier = "^24.8.0" +ruff = "*" +Markdown = "*" +# Rendering docs to HTML +mkdocs = "1.6.1" +# Embedding YAML files into Markdown documents as tables +markdown-data-tables = "1.0.0" +# Render custom markdown for version added/changed/remove notes +markdown-version-annotations = "1.0.1" +# Automatically generate some files as part of mkdocs build +mkdocs-gen-files = "0.5.0" +# Image lightboxing in mkdocs +mkdocs-glightbox = "0.4.0" +# Use Jinja2 templating in docs - see settings.md +mkdocs-macros-plugin = "1.3.7" +# Material for mkdocs theme +mkdocs-material = "9.6.15" +# Handle docs redirections +mkdocs-redirects = "1.2.2" +# Automatically handle index pages for docs sections +mkdocs-section-index = "0.3.10" +# Automatic documentation from sources, for MkDocs +mkdocstrings = "0.27.0" +# Python-specific extension to mkdocstrings +mkdocstrings-python = "1.13.0" + +[tool.ruff] line-length = 120 -target-version = ['py37'] -include = '\.pyi?$' -exclude = ''' -( - /( - \.eggs # exclude a few common directories in the - | \.git # root of the project - | \.hg - | \.mypy_cache - | \.tox - | \.venv - | _build - | buck-out - | build - | dist - )/ - | settings.py # This is where you define files that should not be stylized by black - # the root of the project -) -''' +target-version = "py39" + +[tool.ruff.lint] +select = [ + "D", # pydocstyle + "F", "E", "W", # flake8 + "S", # bandit + "I", # isort +] +ignore = [ + # warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible. + "D203", # 1 blank line required before class docstring + + # D212 is enabled by default in google convention, and complains if we have a docstring like: + # """ + # My docstring is on the line after the opening quotes instead of on the same line as them. + # """ + # We've discussed and concluded that we consider this to be a valid style choice. + "D212", # Multi-line docstring summary should start at the first line + "D213", # Multi-line docstring summary should start at the second line + + # Produces a lot of issues in the current codebase. + "D401", # First line of docstring should be in imperative mood + "D407", # Missing dashed underline after section + "D416", # Section name ends in colon + "E501", # Line too long +] + +[tool.ruff.lint.pydocstyle] +convention = "google" + +[tool.ruff.lint.per-file-ignores] +"tests/*" = [ + "D", + "S" +] + + +[tool.pylint.master] +ignore=[".venv", "tests"] [tool.pylint.basic] # No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes. -no-docstring-rgx="^(_|test_|Meta$)" +no-docstring-rgx = "^(_|test_)" + [tool.pylint.messages_control] -# Line length is enforced by Black, so pylint doesn't need to check it. -# Pylint and Black disagree about how to format multi-line arrays; Black wins. -# too-many-branches disabled to supported nested logic in Operator. -# protected-access disabled as we want test the method -disable = """, - line-too-long, - too-many-branches, - protected-access - """ +disable = [ + "line-too-long", + "too-many-branches", + "protected-access" + ] [tool.pylint.miscellaneous] # Don't flag TODO as a failure, let us commit with things that still need to be done in the code @@ -88,40 +129,66 @@ notes = """, XXX, """ -[tool.pydocstyle] -convention = "google" -inherit = false -match = "(?!__init__).*\\.py" -match-dir = "(?!tests|migrations|development)[^\\.].*" -# D212 is enabled by default in google convention, and complains if we have a docstring like: -# """ -# My docstring is on the line after the opening quotes instead of on the same line as them. -# """ -# We've discussed and concluded that we consider this to be a valid style choice. -add_ignore = "D212" - [build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +requires = ["poetry_core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.pytest.ini_options] +python_paths = "./" testpaths = [ - "tests" + "tests/" ] -addopts = "-vv --doctest-modules" +addopts = "-vv --doctest-modules -p no:warnings --ignore-glob='*mock*'" -[tool.mypy] -exclude = [ - '^tasks\.py', -] +[tool.towncrier] +package = "jdiff" +directory = "changes" +filename = "docs/release_notes.md" +template = "towncrier_template.j2" +start_string = "" +issue_format = "[#{issue}](https://github.com/networktocode/jdiff/issues/{issue})" -[[tool.mypy.overrides]] -module = [ - "deepdiff", - "jmespath", -] -ignore_missing_imports = true +[[tool.towncrier.type]] +directory = "security" +name = "Security" +showcontent = true + +[[tool.towncrier.type]] +directory = "added" +name = "Added" +showcontent = true + +[[tool.towncrier.type]] +directory = "changed" +name = "Changed" +showcontent = true + +[[tool.towncrier.type]] +directory = "deprecated" +name = "Deprecated" +showcontent = true + +[[tool.towncrier.type]] +directory = "removed" +name = "Removed" +showcontent = true + +[[tool.towncrier.type]] +directory = "fixed" +name = "Fixed" +showcontent = true + +[[tool.towncrier.type]] +directory = "dependencies" +name = "Dependencies" +showcontent = true + +[[tool.towncrier.type]] +directory = "documentation" +name = "Documentation" +showcontent = true -[[tool.mypy.overrides]] -module = "tests.*" -ignore_errors = true +[[tool.towncrier.type]] +directory = "housekeeping" +name = "Housekeeping" +showcontent = true diff --git a/tasks.py b/tasks.py index 18508fe..7280eac 100644 --- a/tasks.py +++ b/tasks.py @@ -1,18 +1,14 @@ """Tasks for use with Invoke.""" -import os -import sys -from invoke import task -try: - import toml -except ImportError: - sys.exit("Please make sure to `pip install toml` or enable the Poetry shell and run `poetry install`.") +from invoke import Collection, Exit +from invoke import task as invoke_task def is_truthy(arg): """Convert "truthy" strings into Booleans. - Examples: + Examples + -------- >>> is_truthy('yes') True Args: @@ -30,38 +26,69 @@ def is_truthy(arg): raise ValueError(f"Invalid truthy value: `{arg}`") -PYPROJECT_CONFIG = toml.load("pyproject.toml") -TOOL_CONFIG = PYPROJECT_CONFIG["tool"]["poetry"] +# Use pyinvoke configuration for default values, see http://docs.pyinvoke.org/en/stable/concepts/configuration.html +# Variables may be overwritten in invoke.yml or by the environment variables INVOKE_JDIFF_xxx +namespace = Collection("jdiff") +namespace.configure( + { + "jdiff": { + "project_name": "jdiff", + "python_ver": "3.12", + "local": False, + "image_name": "jdiff", + "image_ver": "latest", + "pwd": ".", + } + } +) + + +# pylint: disable=keyword-arg-before-vararg +def task(function=None, *args, **kwargs): + """Task decorator to override the default Invoke task decorator and add each task to the invoke namespace.""" + + def task_wrapper(function=None): + """Wrapper around invoke.task to add the task to the namespace as well.""" + if args or kwargs: + task_func = invoke_task(*args, **kwargs)(function) + else: + task_func = invoke_task(function) + namespace.add_task(task_func) + return task_func -# Can be set to a separate Python version to be used for launching or building image -PYTHON_VER = os.getenv("PYTHON_VER", "3.8") -# Name of the docker image/image -IMAGE_NAME = os.getenv("IMAGE_NAME", TOOL_CONFIG["name"]) -# Tag for the image -IMAGE_VER = os.getenv("IMAGE_VER", f"{TOOL_CONFIG['version']}-py{PYTHON_VER}") -# Gather current working directory for Docker commands -PWD = os.getcwd() -# Local or Docker execution provide "local" to run locally without docker execution -INVOKE_LOCAL = is_truthy(os.getenv("INVOKE_LOCAL", False)) # pylint: disable=W1508 + if function: + # The decorator was called with no arguments + return task_wrapper(function) + # The decorator was called with arguments + return task_wrapper -def run_cmd(context, exec_cmd, local=INVOKE_LOCAL): +def run_command(context, exec_cmd, port=None): """Wrapper to run the invoke task commands. Args: context ([invoke.task]): Invoke task object. exec_cmd ([str]): Command to run. - local (bool): Define as `True` to execute locally + port (int): Used to serve local docs. Returns: result (obj): Contains Invoke result from running task. """ - if is_truthy(local): + if is_truthy(context.jdiff.local): print(f"LOCAL - Running command {exec_cmd}") result = context.run(exec_cmd, pty=True) else: - print(f"DOCKER - Running command: {exec_cmd} container: {IMAGE_NAME}:{IMAGE_VER}") - result = context.run(f"docker run -it -v {PWD}:/local {IMAGE_NAME}:{IMAGE_VER} sh -c '{exec_cmd}'", pty=True) + print(f"DOCKER - Running command: {exec_cmd} container: {context.jdiff.image_name}:{context.jdiff.image_ver}") + if port: + result = context.run( + f"docker run -it -p {port} -v {context.jdiff.pwd}:/local {context.jdiff.image_name}:{context.jdiff.image_ver} sh -c '{exec_cmd}'", + pty=True, + ) + else: + result = context.run( + f"docker run -it -v {context.jdiff.pwd}:/local {context.jdiff.image_name}:{context.jdiff.image_ver} sh -c '{exec_cmd}'", + pty=True, + ) return result @@ -75,8 +102,8 @@ def run_cmd(context, exec_cmd, local=INVOKE_LOCAL): ) def build(context, cache=True, force_rm=False, hide=False): """Build a Docker image.""" - print(f"Building image {IMAGE_NAME}:{IMAGE_VER}") - command = f"docker build --tag {IMAGE_NAME}:{IMAGE_VER} --build-arg PYTHON_VER={PYTHON_VER} -f Dockerfile ." + print(f"Building image {context.jdiff.image_name}:{context.jdiff.image_ver}") + command = f"docker build --tag {context.jdiff.image_name}:{context.jdiff.image_ver} --build-arg PYTHON_VER={context.jdiff.python_ver} -f Dockerfile ." if not cache: command += " --no-cache" @@ -85,15 +112,15 @@ def build(context, cache=True, force_rm=False, hide=False): result = context.run(command, hide=hide) if result.exited != 0: - print(f"Failed to build image {IMAGE_NAME}:{IMAGE_VER}\nError: {result.stderr}") + print(f"Failed to build image {context.jdiff.image_name}:{context.jdiff.image_ver}\nError: {result.stderr}") @task def clean(context): """Remove the project specific image.""" - print(f"Attempting to forcefully remove image {IMAGE_NAME}:{IMAGE_VER}") - context.run(f"docker rmi {IMAGE_NAME}:{IMAGE_VER} --force") - print(f"Successfully removed image {IMAGE_NAME}:{IMAGE_VER}") + print(f"Attempting to forcefully remove image {context.jdiff.image_name}:{context.jdiff.image_ver}") + context.run(f"docker rmi {context.jdiff.image_name}:{context.jdiff.image_ver} --force") + print(f"Successfully removed image {context.jdiff.image_name}:{context.jdiff.image_ver}") @task @@ -103,78 +130,124 @@ def rebuild(context): build(context, cache=False) -@task(help={"local": "Run locally or within the Docker container"}) -def pytest(context, local=INVOKE_LOCAL): +@task +def pytest(context, args=""): """Run pytest test cases.""" - exec_cmd = "pytest" - run_cmd(context, exec_cmd, local) - + exec_cmd = f"pytest {args}" + run_command(context, exec_cmd) -@task(help={"local": "Run locally or within the Docker container"}) -def black(context, path=".", local=INVOKE_LOCAL): - """Run black to check that Python files adherence to black standards.""" - exec_cmd = f"black {path}" - run_cmd(context, exec_cmd, local) +@task(aliases=("a",)) +def autoformat(context): + """Run code autoformatting.""" + ruff(context, action=["format"], fix=True) -@task(help={"local": "Run locally or within the Docker container"}) -def flake8(context, path=".", local=INVOKE_LOCAL): - """Run flake8 code analysis.""" - exec_cmd = f"flake8 {path}" - run_cmd(context, exec_cmd, local) +@task( + help={ + "action": "Available values are `['lint', 'format']`. Can be used multiple times. (default: `['lint', 'format']`)", + "target": "File or directory to inspect, repeatable (default: all files in the project will be inspected)", + "fix": "Automatically fix selected actions. May not be able to fix all issues found. (default: False)", + "output_format": "See https://docs.astral.sh/ruff/settings/#output-format for details. (default: `concise`)", + }, + iterable=["action", "target"], +) +def ruff(context, action=None, target=None, fix=False, output_format="concise"): + """Run ruff to perform code formatting and/or linting.""" + if not action: + action = ["lint", "format"] + if not target: + target = ["."] + + exit_code = 0 + + if "format" in action: + command = "ruff format " + if not fix: + command += "--check " + command += " ".join(target) + if not run_command(context, command): + exit_code = 1 + + if "lint" in action: + command = "ruff check " + if fix: + command += "--fix " + command += f"--output-format {output_format} " + command += " ".join(target) + if not run_command(context, command): + exit_code = 1 + + if exit_code != 0: + raise Exit(code=exit_code) -@task(help={"local": "Run locally or within the Docker container"}) -def pylint(context, path=".", local=INVOKE_LOCAL): - """Run pylint code analysis.""" - exec_cmd = f'find {path} -name "*.py" | xargs pylint' - run_cmd(context, exec_cmd, local) +@task +def pylint(context): + """Run pylint for the specified name and Python version. -@task(help={"local": "Run locally or within the Docker container"}) -def yamllint(context, path=".", local=INVOKE_LOCAL): - """Run yamllint to validate formatting adheres to NTC defined YAML standards.""" - exec_cmd = f"yamllint {path}" - run_cmd(context, exec_cmd, local) + Args: + context (obj): Used to run specific commands + """ + exec_cmd = 'find . -name "*.py" | grep -vE "tests/unit" | xargs pylint' + run_command(context, exec_cmd) -@task(help={"local": "Run locally or within the Docker container"}) -def pydocstyle(context, path=".", local=INVOKE_LOCAL): - """Run pydocstyle to validate docstring formatting adheres to NTC defined standards.""" - exec_cmd = f"pydocstyle {path}" - run_cmd(context, exec_cmd, local) +@task +def yamllint(context): + """Run yamllint to validate formatting adheres to NTC defined YAML standards. + Args: + context (obj): Used to run specific commands + """ + exec_cmd = "yamllint ." + run_command(context, exec_cmd) -@task(help={"local": "Run locally or within the Docker container"}) -def bandit(context, path=".", local=INVOKE_LOCAL): - """Run bandit to validate basic static code security analysis.""" - exec_cmd = f"bandit --recursive ./{path} --configfile .bandit.yml" - run_cmd(context, exec_cmd, local) +@task +def cli(context): + """Enter the image to perform troubleshooting or dev work. -@task(help={"local": "Run locally or within the Docker container"}) -def mypy(context, path=".", local=INVOKE_LOCAL): - """Run mypy to validate type hinting.""" - exec_cmd = f"mypy {path}" - run_cmd(context, exec_cmd, local) + Args: + context (obj): Used to run specific commands + """ + dev = f"docker run -it -v {context.jdiff.pwd}:/local {context.jdiff.image_name}:{context.jdiff.image_ver} /bin/bash" + context.run(f"{dev}", pty=True) @task -def cli(context): - """Enter the image to perform troubleshooting or dev work.""" - dev = f"docker run -it -v {PWD}:/local {IMAGE_NAME}:{IMAGE_VER} /bin/bash" - context.run(f"{dev}", pty=True) +def tests(context): + """Run all tests for the specified name and Python version. + Args: + context (obj): Used to run specific commands + """ + ruff(context) + pylint(context) + yamllint(context) + pytest(context) -@task(help={"local": "Run locally or within the Docker container"}) -def tests(context, path=".", local=INVOKE_LOCAL): - """Run all tests for this repository.""" - black(context, path, local) - flake8(context, path, local) - pylint(context, path, local) - yamllint(context, path, local) - pydocstyle(context, path, local) - bandit(context, path, local) - mypy(context, path, local) - pytest(context, local) print("All tests have passed!") + + +@task +def docs(context): + """Build and serve docs locally for development.""" + exec_cmd = "mkdocs serve -v --dev-addr=0.0.0.0:8001" + run_command(context, exec_cmd, port="8001:8001") + + +@task( + help={ + "version": "Version of jdiff to generate the release notes for.", + } +) +def generate_release_notes(context, version=""): + """Generate Release Notes using Towncrier.""" + command = "poetry run towncrier build" + if version: + command += f" --version {version}" + else: + command += " --version `poetry version -s`" + # Due to issues with git repo ownership in the containers, this must always run locally. + context.run(command) diff --git a/tests/test_data_normalization.py b/tests/test_data_normalization.py index afce4b1..c0e79a9 100644 --- a/tests/test_data_normalization.py +++ b/tests/test_data_normalization.py @@ -1,8 +1,10 @@ "Flatten list unit test" + import pytest + from jdiff.utils.data_normalization import flatten_list -from .utility import ASSERT_FAIL_MESSAGE +from .utility import ASSERT_FAIL_MESSAGE flatten_list_case_1 = ( [[[[-1, 0], [-1, 0]]]], diff --git a/tests/test_diff_generator.py b/tests/test_diff_generator.py index 9c0892d..18e620f 100644 --- a/tests/test_diff_generator.py +++ b/tests/test_diff_generator.py @@ -1,9 +1,11 @@ """Diff generator tests.""" + import pytest -from jdiff.evaluators import diff_generator + from jdiff import extract_data_from_json -from .utility import load_mocks, ASSERT_FAIL_MESSAGE +from jdiff.evaluators import diff_generator +from .utility import ASSERT_FAIL_MESSAGE, load_mocks # Diff test case 1. global_peers = ( diff --git a/tests/test_diff_helpers.py b/tests/test_diff_helpers.py index 82a23fa..b4a563a 100644 --- a/tests/test_diff_helpers.py +++ b/tests/test_diff_helpers.py @@ -1,5 +1,17 @@ """DIff helpers unit tests.""" -from jdiff.utils.diff_helpers import dict_merger, group_value, fix_deepdiff_key_names, get_diff_iterables_items + +import pytest + +from jdiff import extract_data_from_json +from jdiff.check_types import CheckType +from jdiff.utils.diff_helpers import ( + _parse_index_element_string, + dict_merger, + fix_deepdiff_key_names, + get_diff_iterables_items, + group_value, + parse_diff, +) def test_dict_merger(): @@ -37,10 +49,149 @@ def test_get_diff_iterables_items(): diff_result = { "values_changed": {"root['Ethernet1'][0]['port']": {"new_value": "518", "old_value": "519"}}, "iterable_item_added": { - "root['Ethernet3'][1]": {"hostname": "ios-xrv-unittest", "port": "Gi0/0/0/0"}, + "root['Ethernet3'][1]": { + "hostname": "ios-xrv-unittest", + "port": "Gi0/0/0/0", + }, }, } result = get_diff_iterables_items(diff_result) assert list(dict(result).keys())[0] == "['Ethernet3']" assert list(list(dict(result).values())[0].values())[0] == [{"hostname": "ios-xrv-unittest", "port": "Gi0/0/0/0"}] + + +index_element_case_1 = ( + "index_element['foo']['ip name']", + {"ip name": ""}, +) + +index_element_case_2 = ( + "index_element['foo']['ip name']['ip domain']", + {"ip name": "", "ip domain": ""}, +) + + +index_element_tests = [index_element_case_1, index_element_case_2] + + +@pytest.mark.parametrize("index_element, result", index_element_tests) +def test__parse_index_element_string(index_element, result): + """Test that index_element can be unpacked.""" + _, parsed_result = _parse_index_element_string(index_element) + assert parsed_result == result + + +parse_diff_simple_1 = ( + {"foo": {"bar-1": "baz1"}}, # actual + {"foo": {"bar-2": "baz2"}}, # intended + "foo", # match_config + {"bar-1": "baz1"}, # extra + {"bar-2": "baz2"}, # missing +) + +parse_diff_case_1 = ( + {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos-actual"}}, # actual + {"openconfig-system:config": {"hostname": "veos-intended"}}, # intended + '"openconfig-system:config"', # match_config + {"hostname": "veos-actual", "domain-name": "ntc.com"}, # extra + {"hostname": "veos-intended"}, # missing +) + +parse_diff_case_2 = ( + {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos-0"}}, # actual + {"openconfig-system:config": {"hostname": "veos", "ip name": "ntc.com"}}, # intended + '"openconfig-system:config"', # match_config + {"domain-name": "ntc.com", "hostname": "veos-0"}, # extra + {"hostname": "veos", "ip name": "ntc.com"}, # missing +) + +parse_diff_case_3 = ( + {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos-0"}}, + {"openconfig-system:config": {"ip name": "ntc.com"}}, + '"openconfig-system:config"', + {"domain-name": "ntc.com", "hostname": "veos-0"}, + {"ip name": "ntc.com"}, +) + +parse_diff_case_4 = ( + {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos"}}, + {"openconfig-system:config": {"hostname": "veos"}}, + '"openconfig-system:config"', + {"domain-name": "ntc.com"}, + {}, +) + +parse_diff_case_5 = ( + {"openconfig-system:config": {"domain-name": "ntc.com", "hostname": "veos-0"}}, + {"openconfig-system:config": {"hostname": "veos", "ip name": "ntc.com"}}, + '"openconfig-system:config"', + {"hostname": "veos-0", "domain-name": "ntc.com"}, + {"ip name": "ntc.com", "hostname": "veos"}, +) + +parse_diff_case_6 = ( + {"openconfig-system:ntp": {"servers": {"server": []}}}, + { + "openconfig-system:ntp": { + "servers": { + "server": [ + { + "address": "1.us.pool.ntp.org", + "config": {"address": "1.us.pool.ntp.org"}, + "state": {"address": "1.us.pool.ntp.org"}, + } + ] + } + } + }, + '"openconfig-system:ntp"', + {}, + { + "servers": { + "server": [ + { + "address": "1.us.pool.ntp.org", + "config": { + "address": "1.us.pool.ntp.org", + }, + "state": { + "address": "1.us.pool.ntp.org", + }, + }, + ], + }, + }, +) + +parse_diff_tests = [ + parse_diff_simple_1, + parse_diff_case_1, + parse_diff_case_2, + parse_diff_case_3, + parse_diff_case_4, + parse_diff_case_5, + parse_diff_case_6, +] + + +@pytest.mark.parametrize( + "actual, intended, match_config, extra, missing", + parse_diff_tests, +) +def test_parse_diff(actual, intended, match_config, extra, missing): # pylint: disable=too-many-arguments + """Test that index_element can be unpacked.""" + jdiff_param_match = CheckType.create("exact_match") + extracted_actual = extract_data_from_json(actual, match_config) + extracted_intended = extract_data_from_json(intended, match_config) + jdiff_evaluate_response, _ = jdiff_param_match.evaluate(extracted_intended, extracted_actual) + print("jdiff_evaluate_response", jdiff_evaluate_response) + + parsed_extra, parsed_missing = parse_diff( + jdiff_evaluate_response, + actual, + intended, + match_config, + ) + assert parsed_extra == extra + assert parsed_missing == missing diff --git a/tests/test_filter_parsers.py b/tests/test_filter_parsers.py index fe78f38..c52eec2 100644 --- a/tests/test_filter_parsers.py +++ b/tests/test_filter_parsers.py @@ -1,8 +1,10 @@ "Filter parser unit tests." + import pytest + from jdiff.utils.data_normalization import exclude_filter -from .utility import ASSERT_FAIL_MESSAGE +from .utility import ASSERT_FAIL_MESSAGE exclude_filter_test_case_1 = ( ["interfaceStatistics"], diff --git a/tests/test_get_value.py b/tests/test_get_value.py index e437c55..6304b48 100644 --- a/tests/test_get_value.py +++ b/tests/test_get_value.py @@ -1,8 +1,10 @@ """Test extract_data_from_json.""" + import pytest + from jdiff import extract_data_from_json -from .utility import load_json_file, ASSERT_FAIL_MESSAGE +from .utility import ASSERT_FAIL_MESSAGE, load_json_file test_cases_extract_data_none = [ "global[*]", diff --git a/tests/test_jmespath_parsers.py b/tests/test_jmespath_parsers.py index 972acc1..da793f1 100644 --- a/tests/test_jmespath_parsers.py +++ b/tests/test_jmespath_parsers.py @@ -1,14 +1,16 @@ """jmespath parser unit tests.""" + import pytest + from jdiff.utils.jmespath_parsers import ( - jmespath_value_parser, + associate_key_of_my_value, jmespath_refkey_parser, + jmespath_value_parser, keys_values_zipper, - associate_key_of_my_value, multi_reference_keys, ) -from .utility import load_json_file, ASSERT_FAIL_MESSAGE +from .utility import ASSERT_FAIL_MESSAGE, load_json_file value_parser_case_1 = ( "result[0].vrfs.default.peerList[*].[$peerAddress$,prefixesReceived]", diff --git a/tests/test_operators.py b/tests/test_operators.py index 3d5fd1f..60142fe 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -1,7 +1,10 @@ """Unit tests for operator check-type.""" + import pytest + from jdiff import CheckType, extract_data_from_json -from .utility import load_json_file, ASSERT_FAIL_MESSAGE + +from .utility import ASSERT_FAIL_MESSAGE, load_json_file operator_all_same = ( "pre.json", diff --git a/tests/test_sw_upgrade_device_state.py b/tests/test_sw_upgrade_device_state.py index 05c34b8..85ff932 100644 --- a/tests/test_sw_upgrade_device_state.py +++ b/tests/test_sw_upgrade_device_state.py @@ -1,7 +1,11 @@ """Tests for typical software upgrade device state check.""" + from copy import deepcopy + import pytest + from jdiff import CheckType, extract_data_from_json + from .utility import load_json_file @@ -86,9 +90,9 @@ def test_show_ip_route_missing_and_additional_routes(platform, command): check = CheckType.create("exact_match") eval_results_missing, passed_missing = check.evaluate(command_post[:30], command_pre) eval_results_additional, passed_additional = check.evaluate(command_post, command_pre[:30]) - assert ( - passed_missing is False and passed_additional is False - ), f"FAILED, eval_results_missing: {eval_results_missing}; eval_results_additional: {eval_results_additional}" + assert passed_missing is False and passed_additional is False, ( + f"FAILED, eval_results_missing: {eval_results_missing}; eval_results_additional: {eval_results_additional}" + ) @pytest.mark.parametrize( diff --git a/tests/test_type_checks.py b/tests/test_type_checks.py index 7345cde..a32e237 100644 --- a/tests/test_type_checks.py +++ b/tests/test_type_checks.py @@ -1,15 +1,18 @@ """Check Type unit tests.""" + import pytest + +from jdiff import extract_data_from_json from jdiff.check_types import ( CheckType, ExactMatchType, OperatorType, - ToleranceType, ParameterMatchType, RegexType, + ToleranceType, ) -from jdiff import extract_data_from_json -from .utility import load_json_file, load_mocks, ASSERT_FAIL_MESSAGE + +from .utility import ASSERT_FAIL_MESSAGE, load_json_file, load_mocks def test_child_class_raises_exception(): @@ -21,9 +24,9 @@ class CheckTypeChild(CheckType): with pytest.raises(TypeError) as error: CheckTypeChild() # pylint: disable=E0110 - assert "Can't instantiate abstract class CheckTypeChild" " with abstract methods _validate, evaluate" in str( - error.value - ) + assert "Can't instantiate abstract class CheckTypeChild" in str(error.value) + assert "_validate" in str(error.value) + assert "evaluate" in str(error.value) def test_child_class_proper_implementation(): diff --git a/tests/test_validates.py b/tests/test_validates.py index c10a8ff..0462f7f 100644 --- a/tests/test_validates.py +++ b/tests/test_validates.py @@ -1,5 +1,7 @@ """Unit tests for validator CheckType method.""" + import pytest + from jdiff import CheckType tolerance_wrong_argumet = ( diff --git a/tests/utility.py b/tests/utility.py index f551130..aa1b4e2 100644 --- a/tests/utility.py +++ b/tests/utility.py @@ -3,7 +3,6 @@ import json import os - dirname = os.path.dirname(os.path.abspath(__file__)) diff --git a/towncrier_template.j2 b/towncrier_template.j2 new file mode 100644 index 0000000..a707fac --- /dev/null +++ b/towncrier_template.j2 @@ -0,0 +1,42 @@ + +# v{{ versiondata.version.split(".")[:2] | join(".") }} Release Notes + +This document describes all new features and changes in the release. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Release Overview + +- Major features or milestones +- Changes to compatibility with Nautobot and/or other apps, libraries etc. + +{% if render_title %} +## [v{{ versiondata.version }} ({{ versiondata.date }})](https://github.com/networktocode/jdiff/releases/tag/v{{ versiondata.version}}) + +{% endif %} +{% for section, _ in sections.items() %} +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section] %} +{% if sections[section][category]|length != 0 %} +### {{ definitions[category]['name'] }} + +{% if definitions[category]['showcontent'] %} +{% for text, values in sections[section][category].items() %} +{% for item in text.split('\n') %} +{% if values %} +- {{ values|join(', ') }} - {{ item.strip() }} +{% else %} +- {{ item.strip() }} +{% endif %} +{% endfor %} +{% endfor %} + +{% else %} +- {{ sections[section][category]['']|join(', ') }} + +{% endif %} +{% endif %} +{% endfor %} +{% else %} +No significant changes. + +{% endif %} +{% endfor %}