diff --git a/.github/actions/setup-env/action.yml b/.github/actions/setup-env/action.yml new file mode 100644 index 00000000..4ebcff69 --- /dev/null +++ b/.github/actions/setup-env/action.yml @@ -0,0 +1,52 @@ +name: 'Setup Python and Rust Environment' +description: 'Set up Python and Rust environment for PDM projects with matrix support' + +inputs: + python-version: + description: 'Python version for setup-python' + required: true + default: 3.11 + target: + description: 'Target architecture for maturin' + required: true + default: 'x86_64' + python-target: + description: 'Target architecture for python installation' + required: true + default: 'x64' + +runs: + using: 'composite' + steps: + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + architecture: ${{ matrix.python-target }} + + - name: Setup Rust toolchain + run: rustup component add clippy rustfmt + shell: bash + + - uses: Swatinem/rust-cache@v2 + + - name: Install PDM and dependencies + run: | + python -m pip install pdm + python -m venv .venv + if [ "${{ runner.os }}" = "Windows" ]; then + source .venv/Scripts/Activate + else + source .venv/bin/activate + fi + python -m pip install --upgrade pip + pdm config python.use_venv true + pdm install + shell: bash + + - name: Build and install the project using Maturin + uses: PyO3/maturin-action@v1 + with: + target: ${{ inputs.target }} + command: develop + sccache: 'true' diff --git a/.github/actions/setup-poetry-env/action.yml b/.github/actions/setup-poetry-env/action.yml deleted file mode 100644 index 5683f0dd..00000000 --- a/.github/actions/setup-poetry-env/action.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: "setup-poetry-env" -description: "Composite action to setup the Python and poetry environment." - -inputs: - python-version: - required: false - description: "The python version to use" - default: "3.11" - -runs: - using: "composite" - steps: - - name: Set up python - uses: actions/setup-python@v5 - with: - python-version: ${{ inputs.python-version }} - - - name: Install Poetry - env: - # renovate: datasource=pypi depName=poetry - POETRY_VERSION: "1.8.2" - run: curl -sSL https://install.python-poetry.org | python - -y - shell: bash - - - name: Add Poetry to Path - run: echo "$HOME/.local/bin" >> $GITHUB_PATH - if: ${{ matrix.os != 'Windows' }} - shell: bash - - - name: Add Poetry to Path - run: echo "$APPDATA\Python\Scripts" >> $GITHUB_PATH - if: ${{ matrix.os == 'Windows' }} - shell: bash - - - name: Configure Poetry virtual environment in project - run: poetry config virtualenvs.in-project true - shell: bash - - - name: Load cached venv - id: cached-poetry-dependencies - uses: actions/cache@v4 - with: - path: .venv - key: venv-${{ runner.os }}-${{ inputs.python-version }}-${{ hashFiles('poetry.lock') }} - - - name: Install dependencies - if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: poetry install --no-interaction - shell: bash diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 19137f37..c7fbfb1b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,4 +1,4 @@ -name: Main +name: CI on: pull_request: @@ -6,6 +6,10 @@ on: push: branches: [main] +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: quality: runs-on: ubuntu-latest @@ -13,58 +17,88 @@ jobs: - name: Check out uses: actions/checkout@v4 - - uses: actions/cache@v4 + - name: Set up the environment + uses: ./.github/actions/setup-env with: - path: ~/.cache/pre-commit - key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + python-version: '3.11' + target: 'x86_64' + python-target: 'x64' + + - name: Run pre-commit + run: | + rustup component add rustfmt + pdm run pre-commit run -a --show-diff-on-failure + + - name: Inspect dependencies with deptry + run: | + pdm run deptry python + + linux: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + target: [x86_64, aarch64] + steps: + - name: Check out + uses: actions/checkout@v4 - name: Set up the environment - uses: ./.github/actions/setup-poetry-env + uses: ./.github/actions/setup-env + with: + python-version: ${{ matrix.python-version }} + target: ${{ matrix.target }} + python-target: 'x64' - - name: Run pre-commit - run: poetry run pre-commit run -a --show-diff-on-failure + - name: Check typing + run: pdm run mypy - - name: Inspect dependencies - run: poetry run deptry . + - name: Run tests + run: pdm run pytest tests --cov --cov-config=pyproject.toml --cov-report=xml - - name: Check Poetry lock file consistency - run: poetry lock --check + - name: Upload coverage reports to Codecov with GitHub Action on Python 3.11 and x86_64 + uses: codecov/codecov-action@v3 + if: ${{ matrix.python-version == '3.11' && matrix.target == 'x86_64' }} - tests-and-type-check: - runs-on: ${{ matrix.image }} + windows: + runs-on: windows-latest strategy: matrix: - os: [macOS, Ubuntu, Windows] - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - include: - - os: macOS - image: macos-12 - - os: Ubuntu - image: ubuntu-22.04 - - os: Windows - image: windows-2022 - fail-fast: false - defaults: - run: - shell: bash + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + target: [x64] steps: - name: Check out uses: actions/checkout@v4 - name: Set up the environment - uses: ./.github/actions/setup-poetry-env + uses: ./.github/actions/setup-env with: python-version: ${{ matrix.python-version }} + target: ${{ matrix.target }} + python-target: ${{ matrix.target }} - name: Run tests - run: poetry run pytest tests --cov --cov-config=pyproject.toml --cov-report=xml + run: pdm run pytest tests --cov --cov-config=pyproject.toml --cov-report=xml - - name: Check typing - run: poetry run mypy + macos: + runs-on: macos-latest + strategy: + matrix: + python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] + target: [x86_64, aarch64] + steps: + - name: Check out + uses: actions/checkout@v4 - - name: Upload coverage reports to Codecov with GitHub Action on Python 3.11 for Ubuntu - uses: codecov/codecov-action@v3 - if: ${{ matrix.python-version == '3.11' && matrix.os == 'Ubuntu' }} + - name: Set up the environment + uses: ./.github/actions/setup-env + with: + python-version: ${{ matrix.python-version }} + target: ${{ matrix.target }} + python-target: ${{ matrix.target == 'aarch64' && 'arm64' || 'x64' }} + + - name: Run tests + run: pdm run pytest tests --cov --cov-config=pyproject.toml --cov-report=xml check-docs: runs-on: ubuntu-latest @@ -73,7 +107,10 @@ jobs: uses: actions/checkout@v4 - name: Set up the environment - uses: ./.github/actions/setup-poetry-env + uses: ./.github/actions/setup-env + with: + python-version: '3.11' + target: 'x86_64' - name: Check if documentation can be built - run: poetry run mkdocs build -s + run: pdm run mkdocs build -s diff --git a/.github/workflows/on-release-main.yml b/.github/workflows/on-release-main.yml deleted file mode 100644 index b145b0c0..00000000 --- a/.github/workflows/on-release-main.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: release-main - -on: - release: - types: [published] - branches: [main] - -jobs: - publish: - runs-on: ubuntu-latest - steps: - - name: Check out - uses: actions/checkout@v4 - - - name: Set up the environment - uses: ./.github/actions/setup-poetry-env - - - name: Export tag - id: vars - run: echo tag=${GITHUB_REF#refs/*/} >> $GITHUB_OUTPUT - - - name: Build and publish - run: | - poetry version $RELEASE_VERSION - make build-and-publish - env: - PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} - RELEASE_VERSION: ${{ steps.vars.outputs.tag }} - - deploy-docs: - runs-on: ubuntu-latest - needs: publish - steps: - - name: Check out - uses: actions/checkout@v4 - - - name: Set up the environment - uses: ./.github/actions/setup-poetry-env - - - name: Deploy documentation - run: poetry run mkdocs gh-deploy --force diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..930cf08c --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,181 @@ +name: Release + +on: + release: + types: [published] + +jobs: + set-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Export tag + id: vars + run: echo tag=${GITHUB_REF#refs/*/} >> $GITHUB_OUTPUT + + - name: Update project version + run: | + sed -i "s/^version = \".*\"/version = \"$RELEASE_VERSION\"/" pyproject.toml + env: + RELEASE_VERSION: ${{ steps.vars.outputs.tag }} + + - name: Upload updated pyproject.toml + uses: actions/upload-artifact@v4 + with: + name: pyproject-toml + path: pyproject.toml + + linux: + runs-on: ubuntu-latest + needs: [set-version] + strategy: + matrix: + target: [x86_64, aarch64] + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Download updated pyproject.toml + uses: actions/download-artifact@v4 + with: + name: pyproject-toml + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + architecture: 'x64' + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + manylinux: auto + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-linux-${{ matrix.target }} + path: dist + + windows: + runs-on: windows-latest + needs: [set-version] + strategy: + matrix: + target: [x64] + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Download updated pyproject.toml + uses: actions/download-artifact@v4 + with: + name: pyproject-toml + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + architecture: ${{ matrix.target }} + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-windows-${{ matrix.target }} + path: dist + + macos: + runs-on: macos-latest + needs: [set-version] + strategy: + matrix: + target: [x86_64, aarch64] + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Download updated pyproject.toml + uses: actions/download-artifact@v4 + with: + name: pyproject-toml + + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + architecture: ${{ matrix.target == 'aarch64' && 'arm64' || 'x64' }} + + - name: Build wheels + uses: PyO3/maturin-action@v1 + with: + target: ${{ matrix.target }} + args: --release --out dist --find-interpreter + sccache: 'true' + + - name: Upload wheels + uses: actions/upload-artifact@v4 + with: + name: wheels-macos-${{ matrix.target }} + path: dist + + sdist: + runs-on: ubuntu-latest + needs: [set-version] + steps: + - uses: actions/checkout@v4 + + - name: Download updated pyproject.toml + uses: actions/download-artifact@v4 + with: + name: pyproject-toml + + - name: Build sdist + uses: PyO3/maturin-action@v1 + with: + command: sdist + args: --out dist + + - name: Upload sdist + uses: actions/upload-artifact@v4 + with: + name: wheels-sdist + path: dist + + publish: + name: Publish + runs-on: ubuntu-latest + needs: [linux, windows, macos, sdist] + steps: + - uses: actions/download-artifact@v4 + + - name: Publish to PyPI + uses: PyO3/maturin-action@v1 + env: + MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} + with: + command: upload + args: --non-interactive --skip-existing wheels-*/* + + check-docs: + runs-on: ubuntu-latest + needs: publish + steps: + - name: Check out + uses: actions/checkout@v4 + + - name: Set up the environment + uses: ./.github/actions/setup-env + with: + python-version: '3.11' + target: 'x86_64' + + - name: Deploy documentation + run: pdm run mkdocs gh-deploy --force diff --git a/.gitignore b/.gitignore index 4dc5ec59..9345bd42 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ deptry.json .DS_Store .vscode +.pdm-python + # From https://raw.githubusercontent.com/github/gitignore/main/Python.gitignore Byte-compiled / optimized / DLL files @@ -90,31 +92,6 @@ target/ profile_default/ ipython_config.py -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide .pdm.toml # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee4befb4..f3e1138c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,3 +15,22 @@ repos: - id: ruff args: [--exit-non-zero-on-fix] - id: ruff-format + + - repo: local + hooks: + - id: cargo-fmt + name: cargo fmt + entry: cargo fmt -- + language: system + types: [rust] + pass_filenames: false + + - repo: local + hooks: + - id: cargo-clippy + name: cargo clippy + entry: cargo clippy + args: ["--all-targets", "--all-features", "--", "-D", "warnings"] + language: system + types: [rust] + pass_filenames: false diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..3cd13ea0 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,994 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + +[[package]] +name = "arc-swap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b3d0060af21e8d11a926981cc00c6c1541aa91dd64b9f881985c3da1094425f" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "block-padding", + "generic-array", +] + +[[package]] +name = "block-padding" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chardetng" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14b8f0b65b7b08ae3c8187e8d77174de20cb6777864c6b832d8ad365999cf1ea" +dependencies = [ + "cfg-if", + "encoding_rs", + "memchr", +] + +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cpufeatures" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +dependencies = [ + "libc", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "deptryrs" +version = "0.1.0" +dependencies = [ + "chardetng", + "encoding_rs", + "log", + "pyo3", + "pyo3-log", + "rayon", + "regex", + "rustpython-ast", + "rustpython-parser", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "either" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" + +[[package]] +name = "embed-doc-image" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af36f591236d9d822425cb6896595658fa558fcebf5ee8accac1d4b92c47166e" +dependencies = [ + "base64", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + +[[package]] +name = "is-macro" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7d079e129b77477a49c5c4f1cfe9ce6c2c909ef52520693e8e811a714c7b20" +dependencies = [ + "Inflector", + "pmutil", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "itertools" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lalrpop-util" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "lock_api" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "malachite" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6cf7f4730c30071ba374fac86ad35b1cb7a0716f774737768667ea3fa1828e3" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b06bfa98a4b4802af5a4263b4ad4660e28e51e8490f6354eb9336c70767e1c5" +dependencies = [ + "itertools 0.9.0", + "rand 0.7.3", + "rand_chacha", + "ryu", + "sha3", +] + +[[package]] +name = "malachite-bigint" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a5110aee54537b0cef214efbebdd7df79b7408db8eef4f6a4b6db9d0d8fc01b" +dependencies = [ + "derive_more", + "malachite", + "num-integer", + "num-traits", + "paste", +] + +[[package]] +name = "malachite-nz" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c89e21c64b7af5be3dc8cef16f786243faf59459fe4ba93b44efdeb264e5ade4" +dependencies = [ + "embed-doc-image", + "itertools 0.9.0", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3755e541d5134b5016594c9043094172c4dda9259b3ce824a7b8101941850360" +dependencies = [ + "itertools 0.9.0", + "malachite-base", + "malachite-nz", +] + +[[package]] +name = "memchr" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "opaque-debug" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "phf" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +dependencies = [ + "phf_shared", +] + +[[package]] +name = "phf_codegen" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared", + "rand 0.8.5", +] + +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pmutil" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pyo3" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233" +dependencies = [ + "cfg-if", + "indoc", + "libc", + "memoffset", + "parking_lot", + "portable-atomic", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-log" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c10808ee7250403bedb24bc30c32493e93875fef7ba3e4292226fe924f398bd" +dependencies = [ + "arc-swap", + "log", + "pyo3", +] + +[[package]] +name = "pyo3-macros" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" +dependencies = [ + "heck", + "proc-macro2", + "pyo3-build-config", + "quote", + "syn 2.0.52", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core 0.5.1", + "rand_hc", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + +[[package]] +name = "rayon" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4963ed1bc86e4f3ee217022bd855b297cef07fb9eac5dfa1f788b220b49b3bd" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustpython-ast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcf9438da3660e6b88bd659fdc0cd13bcff4b85c584026a48b800c75bf0f8d00" +dependencies = [ + "is-macro", + "malachite-bigint", + "rustpython-parser-core", + "static_assertions", +] + +[[package]] +name = "rustpython-parser" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db993974ff12f33c5be8a801741463691502f85ead5c503277937c4077bd92a" +dependencies = [ + "anyhow", + "is-macro", + "itertools 0.10.5", + "lalrpop-util", + "log", + "malachite-bigint", + "num-traits", + "phf", + "phf_codegen", + "rustc-hash", + "rustpython-ast", + "rustpython-parser-core", + "tiny-keccak", + "unic-emoji-char", + "unic-ucd-ident", + "unicode_names2", +] + +[[package]] +name = "rustpython-parser-core" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9d560c6dd4dc774d4bbad48c770e074c178c4ed5f6fd0521fcdb639af21bdd" +dependencies = [ + "is-macro", + "memchr", + "rustpython-parser-vendored", +] + +[[package]] +name = "rustpython-parser-vendored" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ae3062d7fe5fe38073f3a1c7145ed9a04e15f6e4a596d642c7db2d5cd2b51b" +dependencies = [ + "memchr", + "once_cell", +] + +[[package]] +name = "ryu" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" + +[[package]] +name = "sha3" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" +dependencies = [ + "block-buffer", + "digest", + "keccak", + "opaque-debug", +] + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "target-lexicon" +version = "0.12.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1fc403891a21bcfb7c37834ba66a547a8f402146eba7265b5a6d88059c9ff2f" + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-emoji-char" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b07221e68897210270a38bde4babb655869637af0f69407f96053a34f76494d" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode_names2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446c96c6dd42604779487f0a981060717156648c1706aa1f464677f03c6cc059" + +[[package]] +name = "unindent" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..07afc45f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "deptryrs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "deptry" +crate-type = ["cdylib"] + +[dependencies] +chardetng = "0.1.17" +encoding_rs = "0.8.33" +log = "0.4.21" +pyo3 = { version = "0.20.3", features = ["abi3-py38"] } +pyo3-log = "0.9.0" +rayon = "1.9.0" +regex = "1.10.3" +rustpython-ast = { version = "0.3.0", features = ["visitor"] } +rustpython-parser = "0.3.0" diff --git a/Makefile b/Makefile index 8308955f..86a6b9a4 100644 --- a/Makefile +++ b/Makefile @@ -1,51 +1,36 @@ .PHONY: install -install: ## Install the Poetry environment. - @echo "🚀 Creating virtual environment using Poetry" - @poetry install +install: ## Install the PDM environment. + @echo "🚀 Creating virtual environment using PDM" + @pdm install .PHONY: check check: ## Run code quality tools. - @echo "🚀 Checking Poetry lock file consistency with 'pyproject.toml': Running poetry lock --check" - @poetry lock --check + @echo "🚀 Checking PDM lock file consistency with 'pyproject.toml': Running pdm lock --check" + @pdm lock --check @echo "🚀 Linting code: Running pre-commit" - @poetry run pre-commit run -a + @pdm run pre-commit run -a @echo "🚀 Static type checking: Running mypy" - @poetry run mypy + @pdm run mypy @echo "🚀 Checking for dependency issues: Running deptry" - @poetry run deptry . + @pdm run deptry python .PHONY: test test: ## Test the code with pytest. @echo "🚀 Testing code: Running pytest" - @poetry run pytest --cov --cov-config=pyproject.toml --cov-report=xml + @pdm run pytest --cov --cov-config=pyproject.toml --cov-report=xml .PHONY: build -build: clean-build ## Build wheel and sdist files using Poetry. +build: clean-build ## Build wheel and sdist files using PDM. @echo "🚀 Creating wheel and sdist files" - @poetry build - -.PHONY: clean-build -clean-build: ## clean build artifacts - @rm -rf dist - -.PHONY: publish -publish: ## Publish a release to PyPI. - @echo "🚀 Publishing: Dry run." - @poetry config pypi-token.pypi $(PYPI_TOKEN) - @poetry publish --dry-run - @echo "🚀 Publishing." - @poetry publish - -.PHONY: build-and-publish -build-and-publish: build publish ## Build and publish. + @maturin build .PHONY: docs-test docs-test: ## Test if documentation can be built without warnings or errors. - @poetry run mkdocs build -s + @pdm run mkdocs build -s .PHONY: docs docs: ## Build and serve the documentation. - @poetry run mkdocs serve + @pdm run mkdocs serve .PHONY: help help: ## Show help for the commands. diff --git a/deptry/imports/extract.py b/deptry/imports/extract.py deleted file mode 100644 index d195ab37..00000000 --- a/deptry/imports/extract.py +++ /dev/null @@ -1,44 +0,0 @@ -from __future__ import annotations - -import logging -from collections import defaultdict -from typing import TYPE_CHECKING - -from deptry.imports.extractors import NotebookImportExtractor, PythonImportExtractor - -if TYPE_CHECKING: - from pathlib import Path - - from deptry.imports.extractors.base import ImportExtractor - from deptry.imports.location import Location - - -def get_imported_modules_for_list_of_files(list_of_files: list[Path]) -> dict[str, list[Location]]: - logging.info("Scanning %d %s...", len(list_of_files), "files" if len(list_of_files) > 1 else "file") - - modules: dict[str, list[Location]] = defaultdict(list) - - for file in list_of_files: - for module, locations in get_imported_modules_from_file(file).items(): - for location in locations: - modules[module].append(location) - - logging.debug("All imported modules: %s\n", modules) - - return modules - - -def get_imported_modules_from_file(path_to_file: Path) -> dict[str, list[Location]]: - logging.debug("Scanning %s...", path_to_file) - - modules = _get_extractor_class(path_to_file)(path_to_file).extract_imports() - - logging.debug("Found the following imports in %s: %s", path_to_file, modules) - - return modules - - -def _get_extractor_class(path_to_file: Path) -> type[ImportExtractor]: - if path_to_file.suffix == ".ipynb": - return NotebookImportExtractor - return PythonImportExtractor diff --git a/deptry/imports/extractors/__init__.py b/deptry/imports/extractors/__init__.py deleted file mode 100644 index 3e7659fe..00000000 --- a/deptry/imports/extractors/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -from __future__ import annotations - -from deptry.imports.extractors.notebook_import_extractor import NotebookImportExtractor -from deptry.imports.extractors.python_import_extractor import PythonImportExtractor - -__all__ = ( - "NotebookImportExtractor", - "PythonImportExtractor", -) diff --git a/deptry/imports/extractors/python_import_extractor.py b/deptry/imports/extractors/python_import_extractor.py deleted file mode 100644 index e240e9d8..00000000 --- a/deptry/imports/extractors/python_import_extractor.py +++ /dev/null @@ -1,31 +0,0 @@ -from __future__ import annotations - -import ast -import logging -from dataclasses import dataclass -from typing import TYPE_CHECKING - -from deptry.imports.extractors.base import ImportExtractor - -if TYPE_CHECKING: - from deptry.imports.location import Location - - -@dataclass -class PythonImportExtractor(ImportExtractor): - """Extract import statements from a Python module.""" - - def extract_imports(self) -> dict[str, list[Location]]: - """Extract all imported top-level modules from the Python file.""" - try: - with self.file.open() as python_file: - tree = ast.parse(python_file.read(), str(self.file)) - except (SyntaxError, ValueError): - try: - with self.file.open(encoding=self._get_file_encoding(self.file)) as python_file: - tree = ast.parse(python_file.read(), str(self.file)) - except UnicodeDecodeError: - logging.warning("Warning: File %s could not be decoded. Skipping...", self.file) - return {} - - return self._extract_imports_from_ast(tree) diff --git a/deptry/imports/location.py b/deptry/imports/location.py deleted file mode 100644 index d082c9cb..00000000 --- a/deptry/imports/location.py +++ /dev/null @@ -1,14 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from pathlib import Path - - -@dataclass(frozen=True) -class Location: - file: Path - line: int | None = None - column: int | None = None diff --git a/docs/contributing.md b/docs/contributing.md index b892fa62..ccaee6fc 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -40,7 +40,7 @@ If you are proposing a new feature: ## Get started! -Ready to contribute? Here's how to set up _deptry_ for local development. Please note this documentation assumes you already have [Poetry](https://python-poetry.org/) and [Git](https://git-scm.com/) installed and ready to go. +Ready to contribute? Here's how to set up _deptry_ for local development. Please note this documentation assumes you already have [PDM](https://pdm-project.org/latest/) and [Git](https://git-scm.com/) installed and ready to go. 1. [Fork](https://github.com/fpgmaas/deptry/fork) the _deptry_ repository on GitHub. @@ -62,12 +62,12 @@ Ready to contribute? Here's how to set up _deptry_ for local development. Please Then, install the virtual environment with: ```bash - poetry install + pdm install ``` 4. Install [pre-commit](https://pre-commit.com/) to run linters/formatters at commit time: ```bash - poetry run pre-commit install + pdm run pre-commit install ``` 5. Create a branch for local development: diff --git a/poetry.lock b/pdm.lock similarity index 76% rename from poetry.lock rename to pdm.lock index b896f374..bdaf258c 100644 --- a/poetry.lock +++ b/pdm.lock @@ -1,28 +1,32 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is @generated by PDM. +# It is not intended for manual editing. + +[metadata] +groups = ["default", "dev", "docs", "typing"] +strategy = ["cross_platform", "inherit_metadata"] +lock_version = "4.4.1" +content_hash = "sha256:0d224338d6c21c2dc206b6950ff3c9d62c2f8f691cb75189a7fd14e2362a9a85" [[package]] name = "babel" version = "2.14.0" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Internationalization utilities" +groups = ["docs"] +dependencies = [ + "pytz>=2015.7; python_version < \"3.9\"", +] files = [ {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, ] -[package.dependencies] -pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""} - -[package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] - [[package]] name = "certifi" version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" +requires_python = ">=3.6" +summary = "Python package for providing Mozilla's CA Bundle." +groups = ["docs"] files = [ {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, @@ -31,9 +35,9 @@ files = [ [[package]] name = "cfgv" version = "3.4.0" -description = "Validate configuration and produce human readable error messages." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Validate configuration and produce human readable error messages." +groups = ["dev"] files = [ {file = "cfgv-3.4.0-py2.py3-none-any.whl", hash = "sha256:b7265b1f29fd3316bfcd2b330d63d024f2bfd8bcb8b0272f8e19a504856c48f9"}, {file = "cfgv-3.4.0.tar.gz", hash = "sha256:e52591d4c5f5dead8e0f673fb16db7949d2cfb3f7da4582893288f0ded8fe560"}, @@ -42,9 +46,9 @@ files = [ [[package]] name = "chardet" version = "5.2.0" -description = "Universal encoding detector for Python 3" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Universal encoding detector for Python 3" +groups = ["default"] files = [ {file = "chardet-5.2.0-py3-none-any.whl", hash = "sha256:e1cf59446890a00105fe7b7912492ea04b6e6f06d4b742b2c788469e34c82970"}, {file = "chardet-5.2.0.tar.gz", hash = "sha256:1b3b6ff479a8c414bc3fa2c0852995695c4a026dcd6d0633b2dd092ca39c1cf7"}, @@ -53,9 +57,9 @@ files = [ [[package]] name = "charset-normalizer" version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" +requires_python = ">=3.7.0" +summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +groups = ["docs"] files = [ {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, @@ -103,19 +107,6 @@ files = [ {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, @@ -152,23 +143,23 @@ files = [ [[package]] name = "click" version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Composable command line interface toolkit" +groups = ["default", "docs"] +dependencies = [ + "colorama; platform_system == \"Windows\"", +] files = [ {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, ] -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - [[package]] name = "colorama" version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" +summary = "Cross-platform colored terminal text." +groups = ["default", "dev", "docs"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, @@ -177,9 +168,9 @@ files = [ [[package]] name = "coverage" version = "7.4.3" -description = "Code coverage measurement for Python" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["dev"] files = [ {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, @@ -235,18 +226,77 @@ files = [ {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, ] -[package.dependencies] -tomli = {version = "*", optional = true, markers = "python_full_version <= \"3.11.0a6\" and extra == \"toml\""} - -[package.extras] -toml = ["tomli"] +[[package]] +name = "coverage" +version = "7.4.3" +extras = ["toml"] +requires_python = ">=3.8" +summary = "Code coverage measurement for Python" +groups = ["dev"] +dependencies = [ + "coverage==7.4.3", + "tomli; python_full_version <= \"3.11.0a6\"", +] +files = [ + {file = "coverage-7.4.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8580b827d4746d47294c0e0b92854c85a92c2227927433998f0d3320ae8a71b6"}, + {file = "coverage-7.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:718187eeb9849fc6cc23e0d9b092bc2348821c5e1a901c9f8975df0bc785bfd4"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:767b35c3a246bcb55b8044fd3a43b8cd553dd1f9f2c1eeb87a302b1f8daa0524"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae7f19afe0cce50039e2c782bff379c7e347cba335429678450b8fe81c4ef96d"}, + {file = "coverage-7.4.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba3a8aaed13770e970b3df46980cb068d1c24af1a1968b7818b69af8c4347efb"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ee866acc0861caebb4f2ab79f0b94dbfbdbfadc19f82e6e9c93930f74e11d7a0"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:506edb1dd49e13a2d4cac6a5173317b82a23c9d6e8df63efb4f0380de0fbccbc"}, + {file = "coverage-7.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd6545d97c98a192c5ac995d21c894b581f1fd14cf389be90724d21808b657e2"}, + {file = "coverage-7.4.3-cp310-cp310-win32.whl", hash = "sha256:f6a09b360d67e589236a44f0c39218a8efba2593b6abdccc300a8862cffc2f94"}, + {file = "coverage-7.4.3-cp310-cp310-win_amd64.whl", hash = "sha256:18d90523ce7553dd0b7e23cbb28865db23cddfd683a38fb224115f7826de78d0"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbbe5e739d45a52f3200a771c6d2c7acf89eb2524890a4a3aa1a7fa0695d2a47"}, + {file = "coverage-7.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:489763b2d037b164846ebac0cbd368b8a4ca56385c4090807ff9fad817de4113"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451f433ad901b3bb00184d83fd83d135fb682d780b38af7944c9faeecb1e0bfe"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fcc66e222cf4c719fe7722a403888b1f5e1682d1679bd780e2b26c18bb648cdc"}, + {file = "coverage-7.4.3-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3ec74cfef2d985e145baae90d9b1b32f85e1741b04cd967aaf9cfa84c1334f3"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:abbbd8093c5229c72d4c2926afaee0e6e3140de69d5dcd918b2921f2f0c8baba"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:35eb581efdacf7b7422af677b92170da4ef34500467381e805944a3201df2079"}, + {file = "coverage-7.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8249b1c7334be8f8c3abcaaa996e1e4927b0e5a23b65f5bf6cfe3180d8ca7840"}, + {file = "coverage-7.4.3-cp311-cp311-win32.whl", hash = "sha256:cf30900aa1ba595312ae41978b95e256e419d8a823af79ce670835409fc02ad3"}, + {file = "coverage-7.4.3-cp311-cp311-win_amd64.whl", hash = "sha256:18c7320695c949de11a351742ee001849912fd57e62a706d83dfc1581897fa2e"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b51bfc348925e92a9bd9b2e48dad13431b57011fd1038f08316e6bf1df107d10"}, + {file = "coverage-7.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d6cdecaedea1ea9e033d8adf6a0ab11107b49571bbb9737175444cea6eb72328"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2eccb883368f9e972e216c7b4c7c06cabda925b5f06dde0650281cb7666a30"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c00cdc8fa4e50e1cc1f941a7f2e3e0f26cb2a1233c9696f26963ff58445bac7"}, + {file = "coverage-7.4.3-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9a4a8dd3dcf4cbd3165737358e4d7dfbd9d59902ad11e3b15eebb6393b0446e"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:062b0a75d9261e2f9c6d071753f7eef0fc9caf3a2c82d36d76667ba7b6470003"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ebe7c9e67a2d15fa97b77ea6571ce5e1e1f6b0db71d1d5e96f8d2bf134303c1d"}, + {file = "coverage-7.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:c0a120238dd71c68484f02562f6d446d736adcc6ca0993712289b102705a9a3a"}, + {file = "coverage-7.4.3-cp312-cp312-win32.whl", hash = "sha256:37389611ba54fd6d278fde86eb2c013c8e50232e38f5c68235d09d0a3f8aa352"}, + {file = "coverage-7.4.3-cp312-cp312-win_amd64.whl", hash = "sha256:d25b937a5d9ffa857d41be042b4238dd61db888533b53bc76dc082cb5a15e914"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:28ca2098939eabab044ad68850aac8f8db6bf0b29bc7f2887d05889b17346454"}, + {file = "coverage-7.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:280459f0a03cecbe8800786cdc23067a8fc64c0bd51dc614008d9c36e1659d7e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0cdedd3500e0511eac1517bf560149764b7d8e65cb800d8bf1c63ebf39edd2"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9babb9466fe1da12417a4aed923e90124a534736de6201794a3aea9d98484e"}, + {file = "coverage-7.4.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dec9de46a33cf2dd87a5254af095a409ea3bf952d85ad339751e7de6d962cde6"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:16bae383a9cc5abab9bb05c10a3e5a52e0a788325dc9ba8499e821885928968c"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2c854ce44e1ee31bda4e318af1dbcfc929026d12c5ed030095ad98197eeeaed0"}, + {file = "coverage-7.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ce8c50520f57ec57aa21a63ea4f325c7b657386b3f02ccaedeccf9ebe27686e1"}, + {file = "coverage-7.4.3-cp38-cp38-win32.whl", hash = "sha256:708a3369dcf055c00ddeeaa2b20f0dd1ce664eeabde6623e516c5228b753654f"}, + {file = "coverage-7.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:1bf25fbca0c8d121a3e92a2a0555c7e5bc981aee5c3fdaf4bb7809f410f696b9"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3b253094dbe1b431d3a4ac2f053b6d7ede2664ac559705a704f621742e034f1f"}, + {file = "coverage-7.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77fbfc5720cceac9c200054b9fab50cb2a7d79660609200ab83f5db96162d20c"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6679060424faa9c11808598504c3ab472de4531c571ab2befa32f4971835788e"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4af154d617c875b52651dd8dd17a31270c495082f3d55f6128e7629658d63765"}, + {file = "coverage-7.4.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8640f1fde5e1b8e3439fe482cdc2b0bb6c329f4bb161927c28d2e8879c6029ee"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:69b9f6f66c0af29642e73a520b6fed25ff9fd69a25975ebe6acb297234eda501"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0842571634f39016a6c03e9d4aba502be652a6e4455fadb73cd3a3a49173e38f"}, + {file = "coverage-7.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a78ed23b08e8ab524551f52953a8a05d61c3a760781762aac49f8de6eede8c45"}, + {file = "coverage-7.4.3-cp39-cp39-win32.whl", hash = "sha256:c0524de3ff096e15fcbfe8f056fdb4ea0bf497d584454f344d59fce069d3e6e9"}, + {file = "coverage-7.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:0209a6369ccce576b43bb227dc8322d8ef9e323d089c6f3f26a597b09cb4d2aa"}, + {file = "coverage-7.4.3-pp38.pp39.pp310-none-any.whl", hash = "sha256:7cbde573904625509a3f37b6fecea974e363460b556a627c60dc2f47e2fffa51"}, + {file = "coverage-7.4.3.tar.gz", hash = "sha256:276f6077a5c61447a48d133ed13e759c09e62aff0dc84274a68dc18660104d52"}, +] [[package]] name = "distlib" version = "0.3.8" -description = "Distribution utilities" -optional = false -python-versions = "*" +summary = "Distribution utilities" +groups = ["dev"] files = [ {file = "distlib-0.3.8-py2.py3-none-any.whl", hash = "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784"}, {file = "distlib-0.3.8.tar.gz", hash = "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64"}, @@ -255,70 +305,56 @@ files = [ [[package]] name = "exceptiongroup" version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Backport of PEP 654 (exception groups)" +groups = ["dev"] +marker = "python_version < \"3.11\"" files = [ {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, ] -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "filelock" version = "3.13.1" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "A platform independent file lock." +groups = ["dev"] files = [ {file = "filelock-3.13.1-py3-none-any.whl", hash = "sha256:57dbda9b35157b05fb3e58ee91448612eb674172fab98ee235ccb0b5bee19a1c"}, {file = "filelock-3.13.1.tar.gz", hash = "sha256:521f5f56c50f8426f5e03ad3b281b490a87ef15bc6c526f168290f0c7148d44e"}, ] -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.24)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - [[package]] name = "ghp-import" version = "2.1.0" -description = "Copy your docs directly to the gh-pages branch." -optional = false -python-versions = "*" +summary = "Copy your docs directly to the gh-pages branch." +groups = ["docs"] +dependencies = [ + "python-dateutil>=2.8.1", +] files = [ {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] -[package.dependencies] -python-dateutil = ">=2.8.1" - -[package.extras] -dev = ["flake8", "markdown", "twine", "wheel"] - [[package]] name = "identify" version = "2.5.35" -description = "File identification library for Python" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "File identification library for Python" +groups = ["dev"] files = [ {file = "identify-2.5.35-py2.py3-none-any.whl", hash = "sha256:c4de0081837b211594f8e877a6b4fad7ca32bbfc1a9307fdd61c28bfe923f13e"}, {file = "identify-2.5.35.tar.gz", hash = "sha256:10a7ca245cfcd756a554a7288159f72ff105ad233c7c4b9c6f0f4d108f5f6791"}, ] -[package.extras] -license = ["ukkonen"] - [[package]] name = "idna" version = "3.6" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" +requires_python = ">=3.5" +summary = "Internationalized Domain Names in Applications (IDNA)" +groups = ["docs"] files = [ {file = "idna-3.6-py3-none-any.whl", hash = "sha256:c05567e9c24a6b9faaa835c4821bad0590fbb9d5779e7caa6e1cc4978e7eb24f"}, {file = "idna-3.6.tar.gz", hash = "sha256:9ecdbbd083b06798ae1e86adcbfe8ab1479cf864e4ee30fe4e46a003d12491ca"}, @@ -327,28 +363,24 @@ files = [ [[package]] name = "importlib-metadata" version = "7.0.2" -description = "Read metadata from Python packages" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Read metadata from Python packages" +groups = ["docs"] +marker = "python_version < \"3.10\"" +dependencies = [ + "zipp>=0.5", +] files = [ {file = "importlib_metadata-7.0.2-py3-none-any.whl", hash = "sha256:f4bc4c0c070c490abf4ce96d715f68e95923320370efb66143df00199bb6c100"}, {file = "importlib_metadata-7.0.2.tar.gz", hash = "sha256:198f568f3230878cb1b44fbd7975f87906c22336dba2e4a7f05278c281fbd792"}, ] -[package.dependencies] -zipp = ">=0.5" - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -perf = ["ipython"] -testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-perf (>=0.9.2)", "pytest-ruff (>=0.2.1)"] - [[package]] name = "iniconfig" version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "brain-dead simple config-ini parsing" +groups = ["dev"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -357,44 +389,37 @@ files = [ [[package]] name = "jinja2" version = "3.1.3" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "A very fast and expressive template engine." +groups = ["docs"] +dependencies = [ + "MarkupSafe>=2.0", +] files = [ {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, ] -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - [[package]] name = "markdown" version = "3.5.2" -description = "Python implementation of John Gruber's Markdown." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Python implementation of John Gruber's Markdown." +groups = ["docs"] +dependencies = [ + "importlib-metadata>=4.4; python_version < \"3.10\"", +] files = [ {file = "Markdown-3.5.2-py3-none-any.whl", hash = "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd"}, {file = "Markdown-3.5.2.tar.gz", hash = "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"}, ] -[package.dependencies] -importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["mdx-gh-links (>=0.2)", "mkdocs (>=1.5)", "mkdocs-gen-files", "mkdocs-literate-nav", "mkdocs-nature (>=0.6)", "mkdocs-section-index", "mkdocstrings[python]"] -testing = ["coverage", "pyyaml"] - [[package]] name = "markupsafe" version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Safely add untrusted strings to HTML/XML markup." +groups = ["docs"] 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"}, @@ -426,15 +451,6 @@ files = [ {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"}, @@ -461,9 +477,9 @@ files = [ [[package]] name = "mergedeep" version = "1.3.4" -description = "A deep merge function for 🐍." -optional = false -python-versions = ">=3.6" +requires_python = ">=3.6" +summary = "A deep merge function for 🐍." +groups = ["docs"] files = [ {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, @@ -472,69 +488,60 @@ files = [ [[package]] name = "mkdocs" version = "1.5.3" -description = "Project documentation with Markdown." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Project documentation with Markdown." +groups = ["docs"] +dependencies = [ + "click>=7.0", + "colorama>=0.4; platform_system == \"Windows\"", + "ghp-import>=1.0", + "importlib-metadata>=4.3; python_version < \"3.10\"", + "jinja2>=2.11.1", + "markdown>=3.2.1", + "markupsafe>=2.0.1", + "mergedeep>=1.3.4", + "packaging>=20.5", + "pathspec>=0.11.1", + "platformdirs>=2.2.0", + "pyyaml-env-tag>=0.1", + "pyyaml>=5.1", + "watchdog>=2.0", +] files = [ {file = "mkdocs-1.5.3-py3-none-any.whl", hash = "sha256:3b3a78e736b31158d64dbb2f8ba29bd46a379d0c6e324c2246c3bc3d2189cfc1"}, {file = "mkdocs-1.5.3.tar.gz", hash = "sha256:eb7c99214dcb945313ba30426c2451b735992c73c2e10838f76d09e39ff4d0e2"}, ] -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", markers = "platform_system == \"Windows\""} -ghp-import = ">=1.0" -importlib-metadata = {version = ">=4.3", markers = "python_version < \"3.10\""} -jinja2 = ">=2.11.1" -markdown = ">=3.2.1" -markupsafe = ">=2.0.1" -mergedeep = ">=1.3.4" -packaging = ">=20.5" -pathspec = ">=0.11.1" -platformdirs = ">=2.2.0" -pyyaml = ">=5.1" -pyyaml-env-tag = ">=0.1" -watchdog = ">=2.0" - -[package.extras] -i18n = ["babel (>=2.9.0)"] -min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-import (==1.0)", "importlib-metadata (==4.3)", "jinja2 (==2.11.1)", "markdown (==3.2.1)", "markupsafe (==2.0.1)", "mergedeep (==1.3.4)", "packaging (==20.5)", "pathspec (==0.11.1)", "platformdirs (==2.2.0)", "pyyaml (==5.1)", "pyyaml-env-tag (==0.1)", "typing-extensions (==3.10)", "watchdog (==2.0)"] - [[package]] name = "mkdocs-material" version = "9.5.13" -description = "Documentation that simply works" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Documentation that simply works" +groups = ["docs"] +dependencies = [ + "babel~=2.10", + "colorama~=0.4", + "jinja2~=3.0", + "markdown~=3.2", + "mkdocs-material-extensions~=1.3", + "mkdocs~=1.5.3", + "paginate~=0.5", + "pygments~=2.16", + "pymdown-extensions~=10.2", + "regex>=2022.4", + "requests~=2.26", +] files = [ {file = "mkdocs_material-9.5.13-py3-none-any.whl", hash = "sha256:5cbe17fee4e3b4980c8420a04cc762d8dc052ef1e10532abd4fce88e5ea9ce6a"}, {file = "mkdocs_material-9.5.13.tar.gz", hash = "sha256:d8e4caae576312a88fd2609b81cf43d233cdbe36860d67a68702b018b425bd87"}, ] -[package.dependencies] -babel = ">=2.10,<3.0" -colorama = ">=0.4,<1.0" -jinja2 = ">=3.0,<4.0" -markdown = ">=3.2,<4.0" -mkdocs = ">=1.5.3,<1.6.0" -mkdocs-material-extensions = ">=1.3,<2.0" -paginate = ">=0.5,<1.0" -pygments = ">=2.16,<3.0" -pymdown-extensions = ">=10.2,<11.0" -regex = ">=2022.4" -requests = ">=2.26,<3.0" - -[package.extras] -git = ["mkdocs-git-committers-plugin-2 (>=1.1,<2.0)", "mkdocs-git-revision-date-localized-plugin (>=1.2.4,<2.0)"] -imaging = ["cairosvg (>=2.6,<3.0)", "pillow (>=10.2,<11.0)"] -recommended = ["mkdocs-minify-plugin (>=0.7,<1.0)", "mkdocs-redirects (>=1.2,<2.0)", "mkdocs-rss-plugin (>=1.6,<2.0)"] - [[package]] name = "mkdocs-material-extensions" version = "1.3.1" -description = "Extension pack for Python Markdown and MkDocs Material." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown and MkDocs Material." +groups = ["docs"] files = [ {file = "mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31"}, {file = "mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443"}, @@ -543,9 +550,14 @@ files = [ [[package]] name = "mypy" version = "1.9.0" -description = "Optional static typing for Python" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Optional static typing for Python" +groups = ["typing"] +dependencies = [ + "mypy-extensions>=1.0.0", + "tomli>=1.1.0; python_version < \"3.11\"", + "typing-extensions>=4.1.0", +] files = [ {file = "mypy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f8a67616990062232ee4c3952f41c779afac41405806042a8126fe96e098419f"}, {file = "mypy-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d357423fa57a489e8c47b7c85dfb96698caba13d66e086b412298a1a0ea3b0ed"}, @@ -576,23 +588,12 @@ files = [ {file = "mypy-1.9.0.tar.gz", hash = "sha256:3cc5da0127e6a478cddd906068496a97a7618a21ce9b54bde5bf7e539c7af974"}, ] -[package.dependencies] -mypy-extensions = ">=1.0.0" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = ">=4.1.0" - -[package.extras] -dmypy = ["psutil (>=4.0)"] -install-types = ["pip"] -mypyc = ["setuptools (>=50)"] -reports = ["lxml"] - [[package]] name = "mypy-extensions" version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" +requires_python = ">=3.5" +summary = "Type system extensions for programs checked with the mypy type checker." +groups = ["typing"] 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"}, @@ -601,34 +602,33 @@ files = [ [[package]] name = "nodeenv" version = "1.8.0" -description = "Node.js virtual environment builder" -optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +requires_python = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +summary = "Node.js virtual environment builder" +groups = ["dev"] +dependencies = [ + "setuptools", +] files = [ {file = "nodeenv-1.8.0-py2.py3-none-any.whl", hash = "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec"}, {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] -[package.dependencies] -setuptools = "*" - [[package]] name = "packaging" -version = "23.2" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" +version = "24.0" +requires_python = ">=3.7" +summary = "Core utilities for Python packages" +groups = ["dev", "docs"] files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, + {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, ] [[package]] name = "paginate" version = "0.5.6" -description = "Divides large result sets into pages for easier browsing" -optional = false -python-versions = "*" +summary = "Divides large result sets into pages for easier browsing" +groups = ["docs"] files = [ {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, ] @@ -636,9 +636,9 @@ files = [ [[package]] name = "pathspec" version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Utility library for gitignore style pattern matching of file paths." +groups = ["default", "docs"] files = [ {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, @@ -647,144 +647,123 @@ files = [ [[package]] name = "platformdirs" version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +groups = ["dev", "docs"] files = [ {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, ] -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - [[package]] name = "pluggy" version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "plugin and hook calling mechanisms for python" +groups = ["dev"] files = [ {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, ] -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - [[package]] name = "pre-commit" version = "3.5.0" -description = "A framework for managing and maintaining multi-language pre-commit hooks." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "A framework for managing and maintaining multi-language pre-commit hooks." +groups = ["dev"] +dependencies = [ + "cfgv>=2.0.0", + "identify>=1.0.0", + "nodeenv>=0.11.1", + "pyyaml>=5.1", + "virtualenv>=20.10.0", +] files = [ {file = "pre_commit-3.5.0-py2.py3-none-any.whl", hash = "sha256:841dc9aef25daba9a0238cd27984041fa0467b4199fc4852e27950664919f660"}, {file = "pre_commit-3.5.0.tar.gz", hash = "sha256:5804465c675b659b0862f07907f96295d490822a450c4c40e747d0b1c6ebcb32"}, ] -[package.dependencies] -cfgv = ">=2.0.0" -identify = ">=1.0.0" -nodeenv = ">=0.11.1" -pyyaml = ">=5.1" -virtualenv = ">=20.10.0" - [[package]] name = "pygments" version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Pygments is a syntax highlighting package written in Python." +groups = ["docs"] files = [ {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] -[package.extras] -plugins = ["importlib-metadata"] -windows-terminal = ["colorama (>=0.4.6)"] - [[package]] name = "pymdown-extensions" version = "10.7.1" -description = "Extension pack for Python Markdown." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Extension pack for Python Markdown." +groups = ["docs"] +dependencies = [ + "markdown>=3.5", + "pyyaml", +] files = [ {file = "pymdown_extensions-10.7.1-py3-none-any.whl", hash = "sha256:f5cc7000d7ff0d1ce9395d216017fa4df3dde800afb1fb72d1c7d3fd35e710f4"}, {file = "pymdown_extensions-10.7.1.tar.gz", hash = "sha256:c70e146bdd83c744ffc766b4671999796aba18842b268510a329f7f64700d584"}, ] -[package.dependencies] -markdown = ">=3.5" -pyyaml = "*" - -[package.extras] -extra = ["pygments (>=2.12)"] - [[package]] name = "pytest" version = "8.0.2" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "pytest: simple powerful testing with Python" +groups = ["dev"] +dependencies = [ + "colorama; sys_platform == \"win32\"", + "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", + "iniconfig", + "packaging", + "pluggy<2.0,>=1.3.0", + "tomli>=1.0.0; python_version < \"3.11\"", +] files = [ {file = "pytest-8.0.2-py3-none-any.whl", hash = "sha256:edfaaef32ce5172d5466b5127b42e0d6d35ebbe4453f0e3505d96afd93f6b096"}, {file = "pytest-8.0.2.tar.gz", hash = "sha256:d4051d623a2e0b7e51960ba963193b09ce6daeb9759a451844a21e4ddedfc1bd"}, ] -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.3.0,<2.0" -tomli = {version = ">=1.0.0", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - [[package]] name = "pytest-cov" version = "4.1.0" -description = "Pytest plugin for measuring coverage." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Pytest plugin for measuring coverage." +groups = ["dev"] +dependencies = [ + "coverage[toml]>=5.2.1", + "pytest>=4.6", +] files = [ {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, ] -[package.dependencies] -coverage = {version = ">=5.2.1", extras = ["toml"]} -pytest = ">=4.6" - -[package.extras] -testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] - [[package]] name = "python-dateutil" version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +summary = "Extensions to the standard Python datetime module" +groups = ["docs"] +dependencies = [ + "six>=1.5", +] files = [ {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, ] -[package.dependencies] -six = ">=1.5" - [[package]] name = "pytz" version = "2024.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" +summary = "World timezone definitions, modern and historical" +groups = ["docs"] +marker = "python_version < \"3.9\"" files = [ {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, @@ -793,9 +772,9 @@ files = [ [[package]] name = "pyyaml" version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" +requires_python = ">=3.6" +summary = "YAML parser and emitter for Python" +groups = ["dev", "docs"] files = [ {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, @@ -820,18 +799,6 @@ files = [ {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, @@ -853,23 +820,23 @@ files = [ [[package]] name = "pyyaml-env-tag" version = "0.1" -description = "A custom YAML tag for referencing environment variables in YAML files. " -optional = false -python-versions = ">=3.6" +requires_python = ">=3.6" +summary = "A custom YAML tag for referencing environment variables in YAML files. " +groups = ["docs"] +dependencies = [ + "pyyaml", +] files = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] -[package.dependencies] -pyyaml = "*" - [[package]] name = "regex" version = "2023.12.25" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Alternative regular expression module, to replace re." +groups = ["docs"] files = [ {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, @@ -917,20 +884,6 @@ files = [ {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, @@ -969,46 +922,37 @@ files = [ [[package]] name = "requests" version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Python HTTP for Humans." +groups = ["docs"] +dependencies = [ + "certifi>=2017.4.17", + "charset-normalizer<4,>=2", + "idna<4,>=2.5", + "urllib3<3,>=1.21.1", +] files = [ {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - [[package]] name = "setuptools" version = "69.1.1" -description = "Easily download, build, install, upgrade, and uninstall Python packages" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Easily download, build, install, upgrade, and uninstall Python packages" +groups = ["dev"] files = [ {file = "setuptools-69.1.1-py3-none-any.whl", hash = "sha256:02fa291a0471b3a18b2b2481ed902af520c69e8ae0919c13da936542754b4c56"}, {file = "setuptools-69.1.1.tar.gz", hash = "sha256:5c0806c7d9af348e6dd3777b4f4dbb42c7ad85b190104837488eab9a7c945cf8"}, ] -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] -testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pip (>=19.1)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff (>=0.2.1)", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.2)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] - [[package]] name = "six" version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" +summary = "Python 2 and 3 compatibility utilities" +groups = ["docs"] files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1017,9 +961,10 @@ files = [ [[package]] name = "tomli" version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "A lil' TOML parser" +groups = ["default", "dev", "typing"] +marker = "python_version < \"3.11\"" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, @@ -1028,9 +973,8 @@ files = [ [[package]] name = "types-chardet" version = "5.0.4.6" -description = "Typing stubs for chardet" -optional = false -python-versions = "*" +summary = "Typing stubs for chardet" +groups = ["typing"] files = [ {file = "types-chardet-5.0.4.6.tar.gz", hash = "sha256:caf4c74cd13ccfd8b3313c314aba943b159de562a2573ed03137402b2bb37818"}, {file = "types_chardet-5.0.4.6-py3-none-any.whl", hash = "sha256:ea832d87e798abf1e4dfc73767807c2b7fee35d0003ae90348aea4ae00fb004d"}, @@ -1039,9 +983,10 @@ files = [ [[package]] name = "types-colorama" version = "0.4.15.20240205" -description = "Typing stubs for colorama" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Typing stubs for colorama" +groups = ["typing"] +marker = "sys_platform == \"win32\"" files = [ {file = "types-colorama-0.4.15.20240205.tar.gz", hash = "sha256:7ae4f58d407d387f4f98b24d81e1b7657ec754ea1dc4619ae5bd27f0c367637e"}, {file = "types_colorama-0.4.15.20240205-py3-none-any.whl", hash = "sha256:3ab26dcd76d2f13b1b795ed5c87a1a1a29331ea64cf614bb6ae958a3cebc3a53"}, @@ -1050,9 +995,9 @@ files = [ [[package]] name = "typing-extensions" version = "4.10.0" -description = "Backported and Experimental Type Hints for Python 3.8+" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Backported and Experimental Type Hints for Python 3.8+" +groups = ["typing"] files = [ {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, @@ -1061,46 +1006,36 @@ files = [ [[package]] name = "urllib3" version = "2.2.1" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "HTTP library with thread-safe connection pooling, file post, and more." +groups = ["docs"] files = [ {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, ] -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - [[package]] name = "virtualenv" version = "20.25.1" -description = "Virtual Python Environment builder" -optional = false -python-versions = ">=3.7" +requires_python = ">=3.7" +summary = "Virtual Python Environment builder" +groups = ["dev"] +dependencies = [ + "distlib<1,>=0.3.7", + "filelock<4,>=3.12.2", + "platformdirs<5,>=3.9.1", +] files = [ {file = "virtualenv-20.25.1-py3-none-any.whl", hash = "sha256:961c026ac520bac5f69acb8ea063e8a4f071bcc9457b9c1f28f6b085c511583a"}, {file = "virtualenv-20.25.1.tar.gz", hash = "sha256:e08e13ecdca7a0bd53798f356d5831434afa5b07b93f0abdf0797b7a06ffe197"}, ] -[package.dependencies] -distlib = ">=0.3.7,<1" -filelock = ">=3.12.2,<4" -platformdirs = ">=3.9.1,<5" - -[package.extras] -docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] -test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] - [[package]] name = "watchdog" version = "4.0.0" -description = "Filesystem events monitoring" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Filesystem events monitoring" +groups = ["docs"] files = [ {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:39cb34b1f1afbf23e9562501673e7146777efe95da24fab5707b88f7fb11649b"}, {file = "watchdog-4.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c522392acc5e962bcac3b22b9592493ffd06d1fc5d755954e6be9f4990de932b"}, @@ -1133,25 +1068,14 @@ files = [ {file = "watchdog-4.0.0.tar.gz", hash = "sha256:e3e7065cbdabe6183ab82199d7a4f6b3ba0a438c5a512a68559846ccb76a78ec"}, ] -[package.extras] -watchmedo = ["PyYAML (>=3.10)"] - [[package]] name = "zipp" version = "3.17.0" -description = "Backport of pathlib-compatible object wrapper for zip files" -optional = false -python-versions = ">=3.8" +requires_python = ">=3.8" +summary = "Backport of pathlib-compatible object wrapper for zip files" +groups = ["docs"] +marker = "python_version < \"3.10\"" files = [ {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, ] - -[package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] - -[metadata] -lock-version = "2.0" -python-versions = ">=3.8,<4.0" -content-hash = "2037187102d2d3d18e500177ee14e3fb8a60ba8ed7944cf1bf651a281d6edef4" diff --git a/poetry.toml b/poetry.toml deleted file mode 100644 index ab1033bd..00000000 --- a/poetry.toml +++ /dev/null @@ -1,2 +0,0 @@ -[virtualenvs] -in-project = true diff --git a/pyproject.toml b/pyproject.toml index 59168270..7b19ee6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,54 +1,63 @@ -[tool.poetry] +[project] name = "deptry" version = "0.0.1" description = "A command line utility to check for unused, missing and transitive dependencies in a Python project." -authors = ["Florian Maas "] -maintainers = ["Mathieu Kniewallner "] -repository = "https://github.com/fpgmaas/deptry" -documentation = "https://fpgmaas.github.io/deptry/" -readme = "README.md" -license = "MIT" -packages = [ - { include = "deptry" } -] -keywords = ["dependency", "poetry"] +authors = [{ name = "Florian Maas", email = "fpgmaas@gmail.com" }] +maintainers = [{ name = "Mathieu Kniewallner", email = "mathieu.kniewallner@gmail.com" }] +requires-python = ">=3.8,<4.0" +license = { file = "LICENSE" } classifiers = [ "Development Status :: 3 - Alpha", "Environment :: Console", "Intended Audience :: Developers", - "Programming Language :: Python", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Quality Assurance", + "Programming Language :: Rust", + "Programming Language :: Python", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] +dependencies = [ + "chardet>=4.0.0", + "click>=8.0.0,<9", + "pathspec>=0.9.0", + "colorama>=0.4.6; sys_platform == 'win32'", + "tomli>=2.0.1; python_version < '3.11'" ] -[tool.poetry.urls] -changelog = "https://github.com/fpgmaas/deptry/blob/main/CHANGELOG.md" - -[tool.poetry.dependencies] -python = ">=3.8,<4.0" -chardet = ">=4.0.0" -click = "^8.0.0" -colorama = { version = ">=0.4.6", markers = "sys_platform == 'win32'" } -pathspec = ">=0.9.0" -tomli = { version = "^2.0.1", python = "<3.11" } - -[tool.poetry.group.dev.dependencies] -pre-commit = "3.5.0" -pytest = "8.0.2" -pytest-cov = "4.1.0" +[tool.pdm.dev-dependencies] +dev = [ + "pre-commit==3.5.0", + "pytest==8.0.2", + "pytest-cov==4.1.0", +] +docs = [ + "mkdocs==1.5.3", + "mkdocs-material==9.5.13", +] +typing = [ + "mypy==1.9.0", + "types-chardet==5.0.4.6", + "types-colorama==0.4.15.20240205; sys_platform == 'win32'", +] -[tool.poetry.group.docs.dependencies] -mkdocs = "1.5.3" -mkdocs-material = "9.5.13" +[project.urls] +homepage = "https://fpgmaas.github.io/deptry" +repository = "https://github.com/fpgmaas/deptry" +documentation = "https://fpgmaas.github.io/deptry" +changelog = "https://github.com/fpgmaas/deptry/blob/main/CHANGELOG.md" -[tool.poetry.group.typing.dependencies] -mypy = "1.9.0" -types-chardet = "5.0.4.6" -types-colorama = { version = "0.4.15.20240205", markers = "sys_platform == 'win32'" } +[project.scripts] +deptry = "deptry.cli:deptry" [build-system] -requires = ["poetry-core>=1.0.0"] -build-backend = "poetry.core.masonry.api" +requires = ["maturin>=1.5,<2.0"] +build-backend = "maturin" + +[tool.maturin] +features = ["pyo3/extension-module"] +python-source = "python" +module-name = "deptry.rust" [tool.coverage.report] skip_empty = true @@ -75,10 +84,11 @@ exclude_lines = [ [tool.coverage.run] branch = true -source = ["deptry"] +source = ["python/deptry"] [tool.mypy] -files = ["deptry", "scripts", "tests"] +mypy_path = "python" +files = ["python/deptry", "scripts", "tests"] explicit_package_bases = true exclude = ["tests/data"] disallow_any_unimported = true @@ -102,9 +112,6 @@ extend_exclude = [ [tool.deptry.per_rule_ignores] DEP001 = ["tomllib"] -[tool.poetry.scripts] -deptry = "deptry.cli:deptry" - [tool.pytest.ini_options] addopts = "--doctest-modules" filterwarnings = ["error"] @@ -177,6 +184,7 @@ ignore = [ strict = true [tool.ruff.lint.isort] +known-first-party = ["deptry"] required-imports = ["from __future__ import annotations"] [tool.ruff.lint.per-file-ignores] diff --git a/deptry/__init__.py b/python/deptry/__init__.py similarity index 100% rename from deptry/__init__.py rename to python/deptry/__init__.py diff --git a/deptry/__main__.py b/python/deptry/__main__.py similarity index 100% rename from deptry/__main__.py rename to python/deptry/__main__.py diff --git a/deptry/cli.py b/python/deptry/cli.py similarity index 100% rename from deptry/cli.py rename to python/deptry/cli.py diff --git a/deptry/config.py b/python/deptry/config.py similarity index 100% rename from deptry/config.py rename to python/deptry/config.py diff --git a/deptry/core.py b/python/deptry/core.py similarity index 97% rename from deptry/core.py rename to python/deptry/core.py index 731c30bc..ff8b0ec1 100644 --- a/deptry/core.py +++ b/python/deptry/core.py @@ -12,7 +12,7 @@ from deptry.dependency_getter.requirements_txt import RequirementsTxtDependencyGetter from deptry.dependency_specification_detector import DependencyManagementFormat, DependencySpecificationDetector from deptry.exceptions import IncorrectDependencyFormatError, UnsupportedPythonVersionError -from deptry.imports.extract import get_imported_modules_for_list_of_files +from deptry.imports.extract import get_imported_modules_from_list_of_files from deptry.module import ModuleBuilder, ModuleLocations from deptry.python_file_finder import PythonFileFinder from deptry.reporters import JSONReporter, TextReporter @@ -80,7 +80,7 @@ def run(self) -> None: ).build(), locations, ) - for module, locations in get_imported_modules_for_list_of_files(all_python_files).items() + for module, locations in get_imported_modules_from_list_of_files(all_python_files).items() ] imported_modules_with_locations = [ module_with_locations diff --git a/deptry/dependency.py b/python/deptry/dependency.py similarity index 100% rename from deptry/dependency.py rename to python/deptry/dependency.py diff --git a/deptry/dependency_getter/__init__.py b/python/deptry/dependency_getter/__init__.py similarity index 100% rename from deptry/dependency_getter/__init__.py rename to python/deptry/dependency_getter/__init__.py diff --git a/deptry/dependency_getter/base.py b/python/deptry/dependency_getter/base.py similarity index 100% rename from deptry/dependency_getter/base.py rename to python/deptry/dependency_getter/base.py diff --git a/deptry/dependency_getter/pdm.py b/python/deptry/dependency_getter/pdm.py similarity index 100% rename from deptry/dependency_getter/pdm.py rename to python/deptry/dependency_getter/pdm.py diff --git a/deptry/dependency_getter/pep_621.py b/python/deptry/dependency_getter/pep_621.py similarity index 100% rename from deptry/dependency_getter/pep_621.py rename to python/deptry/dependency_getter/pep_621.py diff --git a/deptry/dependency_getter/poetry.py b/python/deptry/dependency_getter/poetry.py similarity index 100% rename from deptry/dependency_getter/poetry.py rename to python/deptry/dependency_getter/poetry.py diff --git a/deptry/dependency_getter/requirements_txt.py b/python/deptry/dependency_getter/requirements_txt.py similarity index 100% rename from deptry/dependency_getter/requirements_txt.py rename to python/deptry/dependency_getter/requirements_txt.py diff --git a/deptry/dependency_specification_detector.py b/python/deptry/dependency_specification_detector.py similarity index 100% rename from deptry/dependency_specification_detector.py rename to python/deptry/dependency_specification_detector.py diff --git a/deptry/deprecate/ignore_flags.py b/python/deptry/deprecate/ignore_flags.py similarity index 100% rename from deptry/deprecate/ignore_flags.py rename to python/deptry/deprecate/ignore_flags.py diff --git a/deptry/deprecate/skip_flags.py b/python/deptry/deprecate/skip_flags.py similarity index 100% rename from deptry/deprecate/skip_flags.py rename to python/deptry/deprecate/skip_flags.py diff --git a/deptry/exceptions.py b/python/deptry/exceptions.py similarity index 100% rename from deptry/exceptions.py rename to python/deptry/exceptions.py diff --git a/deptry/imports/__init__.py b/python/deptry/imports/__init__.py similarity index 100% rename from deptry/imports/__init__.py rename to python/deptry/imports/__init__.py diff --git a/python/deptry/imports/extract.py b/python/deptry/imports/extract.py new file mode 100644 index 00000000..36701dbd --- /dev/null +++ b/python/deptry/imports/extract.py @@ -0,0 +1,57 @@ +from __future__ import annotations + +import logging +from collections import defaultdict +from typing import TYPE_CHECKING + +from deptry.imports.extractors import NotebookImportExtractor +from deptry.rust import get_imports_from_py_files + +if TYPE_CHECKING: + from pathlib import Path + + from deptry.rust import Location as RustLocation + +from deptry.imports.location import Location + + +def get_imported_modules_from_list_of_files(list_of_files: list[Path]) -> dict[str, list[Location]]: + logging.info("Scanning %d %s...", len(list_of_files), "files" if len(list_of_files) > 1 else "file") + + py_files = [str(file) for file in list_of_files if file.suffix == ".py"] + ipynb_files = [file for file in list_of_files if file.suffix == ".ipynb"] + + modules: dict[str, list[Location]] = defaultdict(list) + + # Process all .py files in parallel using Rust + if py_files: + rust_result = get_imports_from_py_files(py_files) + for module, locations in convert_rust_locations_to_python_locations(rust_result).items(): + modules[module].extend(locations) + + # Process each .ipynb file individually + for file in ipynb_files: + for module, locations in get_imported_modules_from_ipynb_file(file).items(): + modules[module].extend(locations) + + logging.debug("All imported modules: %s\n", modules) + + return modules + + +def get_imported_modules_from_ipynb_file(path_to_file: Path) -> dict[str, list[Location]]: + logging.debug("Scanning %s...", path_to_file) + + modules = NotebookImportExtractor(path_to_file).extract_imports() + + logging.debug("Found the following imports in %s: %s", path_to_file, modules) + return modules + + +def convert_rust_locations_to_python_locations( + imported_modules: dict[str, list[RustLocation]], +) -> dict[str, list[Location]]: + converted_modules: dict[str, list[Location]] = {} + for module, locations in imported_modules.items(): + converted_modules[module] = [Location.from_rust_location_object(loc) for loc in locations] + return converted_modules diff --git a/python/deptry/imports/extractors/__init__.py b/python/deptry/imports/extractors/__init__.py new file mode 100644 index 00000000..ba141ce1 --- /dev/null +++ b/python/deptry/imports/extractors/__init__.py @@ -0,0 +1,5 @@ +from __future__ import annotations + +from deptry.imports.extractors.notebook_import_extractor import NotebookImportExtractor + +__all__ = ("NotebookImportExtractor",) diff --git a/deptry/imports/extractors/base.py b/python/deptry/imports/extractors/base.py similarity index 100% rename from deptry/imports/extractors/base.py rename to python/deptry/imports/extractors/base.py diff --git a/deptry/imports/extractors/notebook_import_extractor.py b/python/deptry/imports/extractors/notebook_import_extractor.py similarity index 100% rename from deptry/imports/extractors/notebook_import_extractor.py rename to python/deptry/imports/extractors/notebook_import_extractor.py diff --git a/python/deptry/imports/location.py b/python/deptry/imports/location.py new file mode 100644 index 00000000..0218110c --- /dev/null +++ b/python/deptry/imports/location.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from deptry.rust import Location as RustLocation + + +@dataclass(frozen=True) +class Location: + file: Path + line: int | None = None + column: int | None = None + + @classmethod + def from_rust_location_object(cls, location: RustLocation) -> Location: + return cls(file=Path(location.file), line=location.line, column=location.column) diff --git a/deptry/module.py b/python/deptry/module.py similarity index 100% rename from deptry/module.py rename to python/deptry/module.py diff --git a/deptry/python_file_finder.py b/python/deptry/python_file_finder.py similarity index 100% rename from deptry/python_file_finder.py rename to python/deptry/python_file_finder.py diff --git a/deptry/reporters/__init__.py b/python/deptry/reporters/__init__.py similarity index 100% rename from deptry/reporters/__init__.py rename to python/deptry/reporters/__init__.py diff --git a/deptry/reporters/base.py b/python/deptry/reporters/base.py similarity index 100% rename from deptry/reporters/base.py rename to python/deptry/reporters/base.py diff --git a/deptry/reporters/json.py b/python/deptry/reporters/json.py similarity index 100% rename from deptry/reporters/json.py rename to python/deptry/reporters/json.py diff --git a/deptry/reporters/text.py b/python/deptry/reporters/text.py similarity index 100% rename from deptry/reporters/text.py rename to python/deptry/reporters/text.py diff --git a/python/deptry/rust.pyi b/python/deptry/rust.pyi new file mode 100644 index 00000000..f4344df9 --- /dev/null +++ b/python/deptry/rust.pyi @@ -0,0 +1,10 @@ +from .rust import Location as RustLocation + +def get_imports_from_py_files(file_paths: list[str]) -> dict[str, list[RustLocation]]: ... +def get_imports_from_py_file(file_path: str) -> dict[str, list[RustLocation]]: ... + +class Location: + file: str + line: int + column: int + def __init__(self, file: str, line: int, column: int) -> None: ... diff --git a/deptry/stdlibs.py b/python/deptry/stdlibs.py similarity index 100% rename from deptry/stdlibs.py rename to python/deptry/stdlibs.py diff --git a/deptry/utils.py b/python/deptry/utils.py similarity index 100% rename from deptry/utils.py rename to python/deptry/utils.py diff --git a/deptry/violations/__init__.py b/python/deptry/violations/__init__.py similarity index 100% rename from deptry/violations/__init__.py rename to python/deptry/violations/__init__.py diff --git a/deptry/violations/base.py b/python/deptry/violations/base.py similarity index 100% rename from deptry/violations/base.py rename to python/deptry/violations/base.py diff --git a/deptry/violations/dep001_missing/__init__.py b/python/deptry/violations/dep001_missing/__init__.py similarity index 100% rename from deptry/violations/dep001_missing/__init__.py rename to python/deptry/violations/dep001_missing/__init__.py diff --git a/deptry/violations/dep001_missing/finder.py b/python/deptry/violations/dep001_missing/finder.py similarity index 100% rename from deptry/violations/dep001_missing/finder.py rename to python/deptry/violations/dep001_missing/finder.py diff --git a/deptry/violations/dep001_missing/violation.py b/python/deptry/violations/dep001_missing/violation.py similarity index 100% rename from deptry/violations/dep001_missing/violation.py rename to python/deptry/violations/dep001_missing/violation.py diff --git a/deptry/violations/dep002_unused/__init__.py b/python/deptry/violations/dep002_unused/__init__.py similarity index 100% rename from deptry/violations/dep002_unused/__init__.py rename to python/deptry/violations/dep002_unused/__init__.py diff --git a/deptry/violations/dep002_unused/finder.py b/python/deptry/violations/dep002_unused/finder.py similarity index 100% rename from deptry/violations/dep002_unused/finder.py rename to python/deptry/violations/dep002_unused/finder.py diff --git a/deptry/violations/dep002_unused/violation.py b/python/deptry/violations/dep002_unused/violation.py similarity index 100% rename from deptry/violations/dep002_unused/violation.py rename to python/deptry/violations/dep002_unused/violation.py diff --git a/deptry/violations/dep003_transitive/__init__.py b/python/deptry/violations/dep003_transitive/__init__.py similarity index 100% rename from deptry/violations/dep003_transitive/__init__.py rename to python/deptry/violations/dep003_transitive/__init__.py diff --git a/deptry/violations/dep003_transitive/finder.py b/python/deptry/violations/dep003_transitive/finder.py similarity index 100% rename from deptry/violations/dep003_transitive/finder.py rename to python/deptry/violations/dep003_transitive/finder.py diff --git a/deptry/violations/dep003_transitive/violation.py b/python/deptry/violations/dep003_transitive/violation.py similarity index 100% rename from deptry/violations/dep003_transitive/violation.py rename to python/deptry/violations/dep003_transitive/violation.py diff --git a/deptry/violations/dep004_misplaced_dev/__init__.py b/python/deptry/violations/dep004_misplaced_dev/__init__.py similarity index 100% rename from deptry/violations/dep004_misplaced_dev/__init__.py rename to python/deptry/violations/dep004_misplaced_dev/__init__.py diff --git a/deptry/violations/dep004_misplaced_dev/finder.py b/python/deptry/violations/dep004_misplaced_dev/finder.py similarity index 100% rename from deptry/violations/dep004_misplaced_dev/finder.py rename to python/deptry/violations/dep004_misplaced_dev/finder.py diff --git a/deptry/violations/dep004_misplaced_dev/violation.py b/python/deptry/violations/dep004_misplaced_dev/violation.py similarity index 100% rename from deptry/violations/dep004_misplaced_dev/violation.py rename to python/deptry/violations/dep004_misplaced_dev/violation.py diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 00000000..6d833ff5 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "1.75" diff --git a/src/file_utils.rs b/src/file_utils.rs new file mode 100644 index 00000000..eb96bf53 --- /dev/null +++ b/src/file_utils.rs @@ -0,0 +1,75 @@ +// file_utils.rs + +use chardetng::EncodingDetector; +use encoding_rs::Encoding; +use pyo3::exceptions::{PyFileNotFoundError, PyIOError}; +use pyo3::prelude::*; +use regex::Regex; +use std::fs; +use std::fs::File; +use std::io::{BufReader, ErrorKind, Read}; +use std::path::Path; + +/// Reads a Python file's content as a `String`. It first attempts to read the file as UTF-8. +/// If reading fails due to an encoding issue, it tries to determine the file's encoding +/// from a Python encoding declaration or by guessing and then reads the file using the detected encoding +pub fn read_file(file_path: &str) -> PyResult { + let path = Path::new(file_path); + + match fs::read_to_string(path) { + Ok(content) => Ok(content), + Err(e) => match e.kind() { + ErrorKind::NotFound => Err(PyFileNotFoundError::new_err(format!( + "File not found: '{}'", + file_path + ))), + ErrorKind::InvalidData => { + let file = File::open(path).unwrap(); + let mut buffer = Vec::new(); + BufReader::new(file).read_to_end(&mut buffer)?; + + let encoding = detect_python_file_encoding_from_regex(&buffer) + .unwrap_or_else(|| guess_encoding(&buffer)); + read_with_encoding(&buffer, encoding) + } + _ => Err(PyIOError::new_err(format!("An error occurred: '{}'", e))), + }, + } +} + +/// Detects the encoding declared in the first or second line of a Python file according to PEP 263. +/// Returns the detected encoding if found; otherwise, returns None. +fn detect_python_file_encoding_from_regex(buffer: &[u8]) -> Option<&'static Encoding> { + let content = String::from_utf8_lossy(buffer); + let re = Regex::new(r"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)").unwrap(); + + for line in content.lines().take(2) { + if let Some(caps) = re.captures(line) { + if let Some(m) = caps.get(1) { + return Encoding::for_label(m.as_str().as_bytes()); + } + } + } + + None +} + +/// Reads the content of a buffer using the specified encoding and returns the content as a `String`. +/// If decoding fails, it returns an error indicating that decoding was unsuccessful. +fn read_with_encoding(buffer: &[u8], encoding: &'static Encoding) -> PyResult { + let (cow, _encoding_used, had_errors) = encoding.decode(buffer); + if had_errors { + return Err(PyIOError::new_err( + "Failed to decode file content with the detected encoding.", + )); + } + Ok(cow.into_owned()) +} + +/// Uses the `EncodingDetector` crate to guess the encoding of a given byte array. +/// Returns the guessed encoding, defaulting to UTF-8 if no conclusive guess can be made. +fn guess_encoding(bytes: &[u8]) -> &'static Encoding { + let mut detector = EncodingDetector::new(); + detector.feed(bytes, true); + detector.guess(None, true) +} diff --git a/src/imports.rs b/src/imports.rs new file mode 100644 index 00000000..25db77c7 --- /dev/null +++ b/src/imports.rs @@ -0,0 +1,151 @@ +use crate::file_utils; +use crate::location; +use crate::visitor; + +use file_utils::read_file; +use location::Location; +use pyo3::exceptions::PySyntaxError; +use pyo3::prelude::*; +use pyo3::types::{PyDict, PyList, PyString}; +use rayon::prelude::*; +use rustpython_ast::Mod; +use rustpython_ast::Visitor; +use rustpython_parser::source_code::LineIndex; +use rustpython_parser::text_size::TextRange; +use rustpython_parser::{parse, Mode}; +use std::collections::HashMap; +use visitor::ImportVisitor; + +/// Processes multiple Python files in parallel to extract import statements and their locations. +/// Accepts a list of file paths and returns a dictionary mapping module names to their import locations. +#[pyfunction] +pub fn get_imports_from_py_files(py: Python, file_paths: Vec<&PyString>) -> PyResult { + let rust_file_paths: Vec = file_paths + .iter() + .map(|py_str| py_str.to_str().unwrap().to_owned()) + .collect(); + + // Process each file in parallel and collect results + let results: PyResult> = rust_file_paths + .par_iter() + .map(|path_str| _get_imports_from_py_file(path_str)) + .collect(); + + let results = results?; + + // Merge results from each thread + let mut all_imports = HashMap::new(); + for file_result in results { + for (module, locations) in file_result { + all_imports + .entry(module) + .or_insert_with(Vec::new) + .extend(locations); + } + } + + convert_to_python_dict(py, all_imports) +} + +/// Processes a single Python file to extract import statements and their locations. +/// Accepts a single file path and returns a dictionary mapping module names to their import locations. +#[pyfunction] +pub fn get_imports_from_py_file(py: Python, file_path: &PyString) -> PyResult { + let path_str = file_path.to_str()?; + let result = _get_imports_from_py_file(path_str)?; + + convert_to_python_dict(py, result) +} + +/// Core helper function that extracts import statements and their locations from the content of a single Python file. +/// Used internally by both parallel and single file processing functions. +fn _get_imports_from_py_file(path_str: &str) -> PyResult>> { + let file_content = match read_file(path_str) { + Ok(content) => content, + Err(_) => { + log::warn!("Warning: File {} could not be read. Skipping...", path_str); + return Ok(HashMap::new()); + } + }; + + let ast = get_ast_from_file_content(&file_content, path_str) + .map_err(|e| PySyntaxError::new_err(format!("Error parsing file {}: {}", path_str, e)))?; + + let imported_modules = extract_imports_from_ast(ast); + + Ok(convert_imports_with_textranges_to_location_objects( + imported_modules, + path_str, + &file_content, + )) +} + +/// Parses the content of a Python file into an abstract syntax tree (AST). +pub fn get_ast_from_file_content(file_content: &str, file_path: &str) -> PyResult { + parse(file_content, Mode::Module, file_path) + .map_err(|e| PySyntaxError::new_err(format!("Error parsing file {}: {}", file_path, e))) +} + +/// Iterates through an AST to identify and collect import statements, and returns them together with their +/// respective TextRange for each occurrence. +fn extract_imports_from_ast(ast: Mod) -> HashMap> { + let mut visitor = ImportVisitor::new(); + + if let Mod::Module(module) = ast { + for stmt in module.body { + visitor.visit_stmt(stmt); + } + } + + visitor.get_imports() +} + +/// Converts textual ranges of import statements into structured location objects. +/// Facilitates the mapping of imports to detailed, file-specific location data (file, line, column). +fn convert_imports_with_textranges_to_location_objects( + imports: HashMap>, + file_path: &str, + source_code: &str, +) -> HashMap> { + let line_index = LineIndex::from_source_text(source_code); + let mut imports_with_locations = HashMap::>::new(); + + for (module, ranges) in imports { + let locations: Vec = ranges + .iter() + .map(|range| { + let start_line = line_index.line_index(range.start()).get() as usize; + let start_col = line_index + .source_location(range.start(), source_code) + .column + .get() as usize; + Location { + file: file_path.to_string(), + line: Some(start_line), + column: Some(start_col), + } + }) + .collect(); + imports_with_locations.insert(module, locations); + } + imports_with_locations +} + +/// Transforms a Rust HashMap containing import data into a Python dictionary suitable for Python-side consumption. +fn convert_to_python_dict( + py: Python<'_>, + imports_with_locations: HashMap>, +) -> PyResult { + let imports_dict = PyDict::new(py); + + for (module, locations) in imports_with_locations { + let py_locations: Vec = locations + .into_iter() + .map(|location| location.into_py(py)) + .collect(); + let locations_list = PyList::new(py, &py_locations); + imports_dict.set_item(module, locations_list)?; + } + + Ok(imports_dict.into()) +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 00000000..defb4cb6 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,20 @@ +// lib.rs + +use pyo3::prelude::*; + +mod file_utils; +mod imports; +mod location; +mod visitor; + +use location::Location; + +#[pymodule] +fn rust(_py: Python, m: &PyModule) -> PyResult<()> { + pyo3_log::init(); // Initialize logging to forward to Python's logger + + m.add_function(wrap_pyfunction!(imports::get_imports_from_py_files, m)?)?; + m.add_function(wrap_pyfunction!(imports::get_imports_from_py_file, m)?)?; + m.add_class::()?; + Ok(()) +} diff --git a/src/location.rs b/src/location.rs new file mode 100644 index 00000000..c7e59d1b --- /dev/null +++ b/src/location.rs @@ -0,0 +1,27 @@ +use pyo3::prelude::*; + +#[pyclass] +#[derive(Clone, Debug)] +pub struct Location { + #[pyo3(get, set)] + pub file: String, + #[pyo3(get, set)] + pub line: Option, + #[pyo3(get, set)] + pub column: Option, +} + +#[pymethods] +impl Location { + #[new] + pub fn new(file: String, line: Option, column: Option) -> Self { + Location { file, line, column } + } + + fn __repr__(&self) -> PyResult { + Ok(format!( + "Location(file='{}', line={:?}, column={:?})", + self.file, self.line, self.column + )) + } +} diff --git a/src/visitor.rs b/src/visitor.rs new file mode 100644 index 00000000..b478acdc --- /dev/null +++ b/src/visitor.rs @@ -0,0 +1,54 @@ +use rustpython_ast::{self, Int, StmtImport, StmtImportFrom, Visitor}; +use rustpython_parser::text_size::TextRange; +use std::collections::HashMap; + +#[derive(Debug, Clone)] +pub struct ImportVisitor { + imports: HashMap>, +} + +impl ImportVisitor { + pub fn new() -> Self { + ImportVisitor { + imports: HashMap::new(), + } + } + + pub fn get_imports(self) -> HashMap> { + self.imports + } +} + +impl Visitor for ImportVisitor { + fn visit_stmt_import(&mut self, node: StmtImport) { + for alias in &node.names { + let top_level_module = get_top_level_module_name(&alias.name); + self.imports + .entry(top_level_module) + .or_default() + .push(alias.range); + } + } + + fn visit_stmt_import_from(&mut self, node: StmtImportFrom) { + let Some(module) = &node.module else { return }; + if node.level != Some(Int::new(0)) { + return; + } + + self.imports + .entry(get_top_level_module_name(module.as_str())) + .or_default() + .push(node.range); + } +} + +/// Extracts the top-level module name from a potentially nested module path. +/// e.g. when a module_name is `foo.bar`, this returns `foo`. +fn get_top_level_module_name(module_name: &str) -> String { + module_name + .split('.') + .next() + .unwrap_or(module_name) + .to_string() +} diff --git a/tests/functional/cli/test_cli.py b/tests/functional/cli/test_cli.py index 3d25aa88..d3d581ec 100644 --- a/tests/functional/cli/test_cli.py +++ b/tests/functional/cli/test_cli.py @@ -53,7 +53,7 @@ def test_cli_returns_error(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -65,7 +65,7 @@ def test_cli_returns_error(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -123,7 +123,7 @@ def test_cli_ignore_notebooks(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -135,7 +135,7 @@ def test_cli_ignore_notebooks(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -207,7 +207,7 @@ def test_cli_exclude(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -219,7 +219,7 @@ def test_cli_exclude(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -277,7 +277,7 @@ def test_cli_extend_exclude(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -289,7 +289,7 @@ def test_cli_extend_exclude(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -335,7 +335,7 @@ def test_cli_known_first_party(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, ] @@ -382,7 +382,7 @@ def test_cli_not_verbose(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -394,7 +394,7 @@ def test_cli_not_verbose(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -441,7 +441,7 @@ def test_cli_verbose(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -453,7 +453,7 @@ def test_cli_verbose(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] @@ -468,8 +468,8 @@ def test_cli_with_no_ansi(poetry_venv_factory: PoetryVenvFactory) -> None: "", f"{Path('pyproject.toml')}: DEP002 'isort' defined as a dependency but not used in the codebase", f"{Path('pyproject.toml')}: DEP002 'requests' defined as a dependency but not used in the codebase", - f"{Path('src/main.py')}:4:0: DEP004 'black' imported but declared as a dev dependency", - f"{Path('src/main.py')}:6:0: DEP001 'white' imported but missing from the dependency definitions", + f"{Path('src/main.py')}:4:8: DEP004 'black' imported but declared as a dev dependency", + f"{Path('src/main.py')}:6:8: DEP001 'white' imported but missing from the dependency definitions", "Found 4 dependency issues.", "", "For more information, see the documentation: https://fpgmaas.github.io/deptry/", @@ -500,12 +500,12 @@ def test_cli_with_not_json_output(poetry_venv_factory: PoetryVenvFactory) -> Non file=Path("pyproject.toml"), ), stylize( - "{BOLD}{file}{RESET}{CYAN}:{RESET}4{CYAN}:{RESET}0{CYAN}:{RESET} {BOLD}{RED}DEP004{RESET} 'black'" + "{BOLD}{file}{RESET}{CYAN}:{RESET}4{CYAN}:{RESET}8{CYAN}:{RESET} {BOLD}{RED}DEP004{RESET} 'black'" " imported but declared as a dev dependency", file=Path("src/main.py"), ), stylize( - "{BOLD}{file}{RESET}{CYAN}:{RESET}6{CYAN}:{RESET}0{CYAN}:{RESET} {BOLD}{RED}DEP001{RESET} 'white'" + "{BOLD}{file}{RESET}{CYAN}:{RESET}6{CYAN}:{RESET}8{CYAN}:{RESET} {BOLD}{RED}DEP001{RESET} 'white'" " imported but missing from the dependency definitions", file=Path("src/main.py"), ), @@ -540,12 +540,12 @@ def test_cli_with_json_output(poetry_venv_factory: PoetryVenvFactory) -> None: file=Path("pyproject.toml"), ), stylize( - "{BOLD}{file}{RESET}{CYAN}:{RESET}4{CYAN}:{RESET}0{CYAN}:{RESET} {BOLD}{RED}DEP004{RESET} 'black'" + "{BOLD}{file}{RESET}{CYAN}:{RESET}4{CYAN}:{RESET}8{CYAN}:{RESET} {BOLD}{RED}DEP004{RESET} 'black'" " imported but declared as a dev dependency", file=Path("src/main.py"), ), stylize( - "{BOLD}{file}{RESET}{CYAN}:{RESET}6{CYAN}:{RESET}0{CYAN}:{RESET} {BOLD}{RED}DEP001{RESET} 'white'" + "{BOLD}{file}{RESET}{CYAN}:{RESET}6{CYAN}:{RESET}8{CYAN}:{RESET} {BOLD}{RED}DEP001{RESET} 'white'" " imported but missing from the dependency definitions", file=Path("src/main.py"), ), @@ -591,7 +591,7 @@ def test_cli_with_json_output(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -603,7 +603,7 @@ def test_cli_with_json_output(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_multiple_source_directories.py b/tests/functional/cli/test_cli_multiple_source_directories.py index 701c59b3..a33e64f5 100644 --- a/tests/functional/cli/test_cli_multiple_source_directories.py +++ b/tests/functional/cli/test_cli_multiple_source_directories.py @@ -25,11 +25,11 @@ def test_cli_with_multiple_source_directories(pip_venv_factory: PipVenvFactory) { "error": {"code": "DEP001", "message": "'httpx' imported but missing from the dependency definitions"}, "module": "httpx", - "location": {"file": str(Path("src/foo.py")), "line": 1, "column": 0}, + "location": {"file": str(Path("src/foo.py")), "line": 1, "column": 8}, }, { "error": {"code": "DEP001", "message": "'celery' imported but missing from the dependency definitions"}, "module": "celery", - "location": {"file": str(Path("worker/foo.py")), "line": 1, "column": 0}, + "location": {"file": str(Path("worker/foo.py")), "line": 1, "column": 8}, }, ] diff --git a/tests/functional/cli/test_cli_pdm.py b/tests/functional/cli/test_cli_pdm.py index 5b3a35b2..6c6a398f 100644 --- a/tests/functional/cli/test_cli_pdm.py +++ b/tests/functional/cli/test_cli_pdm.py @@ -50,7 +50,7 @@ def test_cli_with_pdm(pdm_venv_factory: PDMVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -62,7 +62,7 @@ def test_cli_with_pdm(pdm_venv_factory: PDMVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, { @@ -74,7 +74,7 @@ def test_cli_with_pdm(pdm_venv_factory: PDMVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 7, - "column": 0, + "column": 8, }, }, { @@ -86,7 +86,7 @@ def test_cli_with_pdm(pdm_venv_factory: PDMVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 8, - "column": 0, + "column": 8, }, }, { @@ -98,7 +98,7 @@ def test_cli_with_pdm(pdm_venv_factory: PDMVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 9, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_pep_621.py b/tests/functional/cli/test_cli_pep_621.py index d7a98514..15ab5d13 100644 --- a/tests/functional/cli/test_cli_pep_621.py +++ b/tests/functional/cli/test_cli_pep_621.py @@ -74,7 +74,7 @@ def test_cli_with_pep_621(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_poetry.py b/tests/functional/cli/test_cli_poetry.py index 7d7872a6..42c7216a 100644 --- a/tests/functional/cli/test_cli_poetry.py +++ b/tests/functional/cli/test_cli_poetry.py @@ -50,7 +50,7 @@ def test_cli_with_poetry(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -62,7 +62,7 @@ def test_cli_with_poetry(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, { @@ -74,7 +74,7 @@ def test_cli_with_poetry(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 7, - "column": 0, + "column": 8, }, }, { @@ -86,7 +86,7 @@ def test_cli_with_poetry(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 8, - "column": 0, + "column": 8, }, }, { @@ -98,7 +98,7 @@ def test_cli_with_poetry(poetry_venv_factory: PoetryVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 9, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_pyproject_different_directory.py b/tests/functional/cli/test_cli_pyproject_different_directory.py index 00af98ad..c608e668 100644 --- a/tests/functional/cli/test_cli_pyproject_different_directory.py +++ b/tests/functional/cli/test_cli_pyproject_different_directory.py @@ -76,7 +76,7 @@ def test_cli_with_pyproject_different_directory(pip_venv_factory: PipVenvFactory "location": { "file": str(Path("src/project_with_src_directory/foo.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_requirements_txt.py b/tests/functional/cli/test_cli_requirements_txt.py index d3e456d9..2e275a01 100644 --- a/tests/functional/cli/test_cli_requirements_txt.py +++ b/tests/functional/cli/test_cli_requirements_txt.py @@ -58,7 +58,7 @@ def test_cli_single_requirements_txt(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -70,7 +70,7 @@ def test_cli_single_requirements_txt(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, { @@ -82,7 +82,7 @@ def test_cli_single_requirements_txt(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/main.py")), "line": 7, - "column": 0, + "column": 1, }, }, { @@ -148,7 +148,7 @@ def test_cli_multiple_requirements_txt(pip_venv_factory: PipVenvFactory) -> None "location": { "file": str(Path("src/main.py")), "line": 4, - "column": 0, + "column": 8, }, }, { @@ -160,7 +160,7 @@ def test_cli_multiple_requirements_txt(pip_venv_factory: PipVenvFactory) -> None "location": { "file": str(Path("src/main.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/functional/cli/test_cli_src_directory.py b/tests/functional/cli/test_cli_src_directory.py index ccf165c2..da6056ff 100644 --- a/tests/functional/cli/test_cli_src_directory.py +++ b/tests/functional/cli/test_cli_src_directory.py @@ -74,7 +74,7 @@ def test_cli_with_src_directory(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/foobar.py")), "line": 1, - "column": 0, + "column": 8, }, }, { @@ -86,7 +86,7 @@ def test_cli_with_src_directory(pip_venv_factory: PipVenvFactory) -> None: "location": { "file": str(Path("src/project_with_src_directory/foo.py")), "line": 6, - "column": 0, + "column": 8, }, }, ] diff --git a/tests/unit/imports/test_extract.py b/tests/unit/imports/test_extract.py index 55f9cc28..05dd79a5 100644 --- a/tests/unit/imports/test_extract.py +++ b/tests/unit/imports/test_extract.py @@ -2,14 +2,14 @@ import json import logging +import re import uuid from pathlib import Path from typing import TYPE_CHECKING -from unittest import mock import pytest -from deptry.imports.extract import get_imported_modules_from_file +from deptry.imports.extract import get_imported_modules_from_ipynb_file, get_imported_modules_from_list_of_files from deptry.imports.location import Location from tests.utils import run_within_dir @@ -20,31 +20,31 @@ def test_import_parser_py() -> None: some_imports_path = Path("tests/data/some_imports.py") - assert get_imported_modules_from_file(some_imports_path) == { - "barfoo": [Location(some_imports_path, 20, 0)], - "baz": [Location(some_imports_path, 16, 4)], - "click": [Location(some_imports_path, 24, 4)], - "foobar": [Location(some_imports_path, 18, 4)], - "httpx": [Location(some_imports_path, 14, 4)], - "module_in_class": [Location(some_imports_path, 35, 8)], - "module_in_func": [Location(some_imports_path, 30, 4)], - "not_click": [Location(some_imports_path, 26, 4)], + assert get_imported_modules_from_list_of_files([some_imports_path]) == { + "barfoo": [Location(some_imports_path, 20, 8)], + "baz": [Location(some_imports_path, 16, 5)], + "click": [Location(some_imports_path, 24, 12)], + "foobar": [Location(some_imports_path, 18, 12)], + "httpx": [Location(some_imports_path, 14, 12)], + "module_in_class": [Location(some_imports_path, 35, 16)], + "module_in_func": [Location(some_imports_path, 30, 12)], + "not_click": [Location(some_imports_path, 26, 12)], "numpy": [ - Location(some_imports_path, 5, 0), - Location(some_imports_path, 7, 0), + Location(some_imports_path, 5, 8), + Location(some_imports_path, 7, 1), ], - "os": [Location(some_imports_path, 1, 0)], - "pandas": [Location(some_imports_path, 6, 0)], - "pathlib": [Location(some_imports_path, 2, 0)], - "randomizer": [Location(some_imports_path, 21, 0)], - "typing": [Location(some_imports_path, 3, 0)], + "os": [Location(some_imports_path, 1, 1)], + "pandas": [Location(some_imports_path, 6, 8)], + "pathlib": [Location(some_imports_path, 2, 1)], + "randomizer": [Location(some_imports_path, 21, 1)], + "typing": [Location(some_imports_path, 3, 1)], } def test_import_parser_ipynb() -> None: notebook_path = Path("tests/data/example_project/src/notebook.ipynb") - assert get_imported_modules_from_file(notebook_path) == { + assert get_imported_modules_from_ipynb_file(notebook_path) == { "click": [Location(notebook_path, 1, 0)], "toml": [Location(notebook_path, 5, 0)], "urllib3": [Location(notebook_path, 3, 0)], @@ -79,7 +79,7 @@ def test_import_parser_file_encodings(file_content: str, encoding: str | None, t with random_file.open("w", encoding=encoding) as f: f.write(file_content) - assert get_imported_modules_from_file(random_file) == {"foo": [Location(random_file, 2, 0)]} + assert get_imported_modules_from_list_of_files([random_file]) == {"foo": [Location(random_file, 2, 8)]} @pytest.mark.parametrize( @@ -119,20 +119,22 @@ def test_import_parser_file_encodings_ipynb(code_cell_content: list[str], encodi } f.write(json.dumps(file_content)) - assert get_imported_modules_from_file(random_file) == {"foo": [Location(random_file, 1, 0)]} + assert get_imported_modules_from_list_of_files([random_file]) == {"foo": [Location(random_file, 1, 0)]} def test_import_parser_file_encodings_warning(tmp_path: Path, caplog: LogCaptureFixture) -> None: file_path = Path("file1.py") with run_within_dir(tmp_path): - with file_path.open("w", encoding="utf-8") as f: - f.write("print('this is a mock unparseable file')") - - with caplog.at_level(logging.WARNING), mock.patch( - "deptry.imports.extractors.python_import_extractor.ast.parse", - side_effect=UnicodeDecodeError("fakecodec", b"\x00\x00", 1, 2, "Fake reason!"), - ): - assert get_imported_modules_from_file(file_path) == {} - - assert "Warning: File file1.py could not be decoded. Skipping..." in caplog.text + # The characters below are represented differently in ISO-8859-1 and UTF-8, so this should raise an error. + with file_path.open("w", encoding="ISO-8859-1") as f: + f.write("# -*- coding: utf-8 -*-\nprint('ÆØÅ')") + + with caplog.at_level(logging.WARNING): + assert get_imported_modules_from_list_of_files([file_path]) == {} + + # //TODO logging from Rust still includes it's own warning and file + line number. Can we get rid of that? + pattern = re.compile( + r"WARNING deptry.imports:imports.rs:\d+ Warning: File file1.py could not be read. Skipping...\n" + ) + assert pattern.search(caplog.text) is not None diff --git a/tox.ini b/tox.ini index b3dc5c79..8c9ebeb8 100644 --- a/tox.ini +++ b/tox.ini @@ -3,9 +3,9 @@ skipsdist = true envlist = py38, py39, py310, py311, py312 [testenv] -allowlist_externals = poetry +allowlist_externals = pdm skip_install = true -commands_pre = poetry install +commands_pre = pdm install commands = - poetry run pytest - poetry run mypy + pdm run pytest + pdm run mypy