├── .editorconfig ├── .github └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── pdm.lock ├── pyproject.toml ├── tests ├── fixture-project │ ├── demo.py │ ├── pdm.lock │ └── pyproject.toml └── test_integration.py ├── tox.ini └── tox_pdm ├── __init__.py ├── plugin.py └── utils.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | insert_final_newline = true 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | strategy: 12 | matrix: 13 | python-version: [3.8, 3.9, "3.10", "3.11", "3.12"] 14 | 15 | steps: 16 | - uses: actions/checkout@v3 17 | - name: Set up Python ${{ matrix.python-version }} 18 | uses: pdm-project/setup-pdm@v3 19 | with: 20 | python-version: ${{ matrix.python-version }} 21 | cache: true 22 | - name: Install dependencies 23 | run: | 24 | python -m pip install --upgrade tox . 25 | - name: Test with tox 26 | run: | 27 | tox --version 28 | pyversion="${{ matrix.python-version }}" 29 | tox -e py${pyversion/./} 30 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "*" 7 | 8 | jobs: 9 | release-pypi: 10 | name: release-pypi 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: actions/setup-python@v5 16 | with: 17 | python-version: "3.10" 18 | architecture: "x64" 19 | 20 | - name: Build artifacts 21 | run: | 22 | pipx run build 23 | - name: Upload to Pypi 24 | run: | 25 | pip install twine 26 | twine upload --username __token__ --password ${{ secrets.PYPI_TOKEN }} dist/* 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pypackages__/ 2 | __pycache__/ 3 | *.py[cod] 4 | .pdm-python 5 | *.egg-info/ 6 | dist/ 7 | build/ 8 | .vscode/ 9 | .tox/ 10 | /.venv/ 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/psf/black 3 | rev: 24.8.0 4 | hooks: 5 | - id: black 6 | - repo: https://github.com/PyCQA/flake8 7 | rev: 7.1.1 8 | hooks: 9 | - id: flake8 10 | 11 | - repo: https://github.com/pycqa/isort 12 | rev: 5.13.2 13 | hooks: 14 | - id: isort 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 pdm-project 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tox-pdm 2 | 3 | A plugin for tox that utilizes PDM as the package manager and installer. 4 | 5 | [![Github Actions](https://github.com/pdm-project/tox-pdm/workflows/Tests/badge.svg)](https://github.com/pdm-project/tox-pdm/actions) 6 | [![PyPI](https://img.shields.io/pypi/v/tox-pdm?logo=python&logoColor=%23cccccc)](https://pypi.org/project/tox-pdm) 7 | [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white)](https://github.com/pre-commit/pre-commit) 8 | [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) 9 | ![Tox Versions](https://img.shields.io/badge/tox-v4-yellowgreen) 10 | 11 | With this plugin, you can migrate your project to PDM while retaining the ability to test against multiple versions. 12 | 13 | ## Installation 14 | 15 | ```console 16 | $ pip install tox-pdm 17 | ``` 18 | 19 | Or, 20 | 21 | ```console 22 | $ pdm add -d tox-pdm 23 | ``` 24 | 25 | Or if you installed `tox` via `pipx`, you can inject the plugin by: 26 | 27 | ```console 28 | $ pipx inject tox tox-pdm 29 | ``` 30 | 31 | ## Example tox.ini 32 | 33 | The following simple example installs `dev` and `test` dependencies into the venv created by Tox and uses pytest to execute the tests, on both Python 3.7 and 3.8. 34 | 35 | ```ini 36 | [tox] 37 | min_version = 4.0 38 | envlist = py37,py38 39 | 40 | [testenv] 41 | groups = ; Dependency groups in pyproject.toml 42 | dev 43 | test 44 | deps = ; Additional dependencies, it will be installed into the library path via normal pip method 45 | flake8 46 | commands = 47 | pytest test/ 48 | ``` 49 | 50 | Here is another one installing the `test` dependencies and executing the `test` PDM script 51 | 52 | ```ini 53 | [tox] 54 | min_version = 4.0 55 | envlist = py3{8,9,10} 56 | 57 | [testenv] 58 | groups = test 59 | commands = test 60 | ``` 61 | 62 | If no groups are specified, the pdm install will be skipped. 63 | 64 | A real-world example can be found at this repository's [tox.ini](https://github.com/pdm-project/tox-pdm/blob/main/tox.ini) and 65 | [GitHub Action workflow](https://github.com/pdm-project/tox-pdm/blob/main/.github/workflows/ci.yml). 66 | 67 | ## Some best practices: 68 | 69 | 1. `pdm` executable must be exposed in `PATH`, if it is not the case, give the absolute path to tox by `tox --pdm `. 70 | 2. Make sure you have generated `pdm.lock` before running the test, it will greatly accelerate the testing. 71 | 3. If you don't set `skip_install = true`, the current package will be built and installed into the testing environment together with the `dependencies` from `pyproject.toml`. 72 | 4. Reuse your PDM scripts to avoid duplication 73 | -------------------------------------------------------------------------------- /pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default", "lint", "test"] 6 | strategy = ["cross_platform", "inherit_metadata"] 7 | lock_version = "4.4.1" 8 | content_hash = "sha256:f011721dacf5dac8ba427289e40fe029b2e1fb93d4c3981354bea40ace107363" 9 | 10 | [[package]] 11 | name = "attrs" 12 | version = "22.1.0" 13 | requires_python = ">=3.5" 14 | summary = "Classes Without Boilerplate" 15 | groups = ["test"] 16 | files = [ 17 | {file = "attrs-22.1.0-py2.py3-none-any.whl", hash = "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c"}, 18 | {file = "attrs-22.1.0.tar.gz", hash = "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6"}, 19 | ] 20 | 21 | [[package]] 22 | name = "black" 23 | version = "22.12.0" 24 | requires_python = ">=3.7" 25 | summary = "The uncompromising code formatter." 26 | groups = ["lint"] 27 | dependencies = [ 28 | "click>=8.0.0", 29 | "mypy-extensions>=0.4.3", 30 | "pathspec>=0.9.0", 31 | "platformdirs>=2", 32 | "tomli>=1.1.0; python_full_version < \"3.11.0a7\"", 33 | "typed-ast>=1.4.2; python_version < \"3.8\" and implementation_name == \"cpython\"", 34 | "typing-extensions>=3.10.0.0; python_version < \"3.10\"", 35 | ] 36 | files = [ 37 | {file = "black-22.12.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9eedd20838bd5d75b80c9f5487dbcb06836a43833a37846cf1d8c1cc01cef59d"}, 38 | {file = "black-22.12.0-cp310-cp310-win_amd64.whl", hash = "sha256:159a46a4947f73387b4d83e87ea006dbb2337eab6c879620a3ba52699b1f4351"}, 39 | {file = "black-22.12.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d30b212bffeb1e252b31dd269dfae69dd17e06d92b87ad26e23890f3efea366f"}, 40 | {file = "black-22.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:7412e75863aa5c5411886804678b7d083c7c28421210180d67dfd8cf1221e1f4"}, 41 | {file = "black-22.12.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c116eed0efb9ff870ded8b62fe9f28dd61ef6e9ddd28d83d7d264a38417dcee2"}, 42 | {file = "black-22.12.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1f58cbe16dfe8c12b7434e50ff889fa479072096d79f0a7f25e4ab8e94cd8350"}, 43 | {file = "black-22.12.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77d86c9f3db9b1bf6761244bc0b3572a546f5fe37917a044e02f3166d5aafa7d"}, 44 | {file = "black-22.12.0-cp38-cp38-win_amd64.whl", hash = "sha256:82d9fe8fee3401e02e79767016b4907820a7dc28d70d137eb397b92ef3cc5bfc"}, 45 | {file = "black-22.12.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:101c69b23df9b44247bd88e1d7e90154336ac4992502d4197bdac35dd7ee3320"}, 46 | {file = "black-22.12.0-cp39-cp39-win_amd64.whl", hash = "sha256:559c7a1ba9a006226f09e4916060982fd27334ae1998e7a38b3f33a37f7a2148"}, 47 | {file = "black-22.12.0-py3-none-any.whl", hash = "sha256:436cc9167dd28040ad90d3b404aec22cedf24a6e4d7de221bec2730ec0c97bcf"}, 48 | {file = "black-22.12.0.tar.gz", hash = "sha256:229351e5a18ca30f447bf724d007f890f97e13af070bb6ad4c0a441cd7596a2f"}, 49 | ] 50 | 51 | [[package]] 52 | name = "cachetools" 53 | version = "5.3.1" 54 | requires_python = ">=3.7" 55 | summary = "Extensible memoizing collections and decorators" 56 | groups = ["default"] 57 | files = [ 58 | {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, 59 | {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, 60 | ] 61 | 62 | [[package]] 63 | name = "chardet" 64 | version = "5.1.0" 65 | requires_python = ">=3.7" 66 | summary = "Universal encoding detector for Python 3" 67 | groups = ["default"] 68 | files = [ 69 | {file = "chardet-5.1.0-py3-none-any.whl", hash = "sha256:362777fb014af596ad31334fde1e8c327dfdb076e1960d1694662d46a6917ab9"}, 70 | {file = "chardet-5.1.0.tar.gz", hash = "sha256:0d62712b956bc154f85fb0a266e2a3c5913c2967e00348701b32411d6def31e5"}, 71 | ] 72 | 73 | [[package]] 74 | name = "click" 75 | version = "8.1.3" 76 | requires_python = ">=3.7" 77 | summary = "Composable command line interface toolkit" 78 | groups = ["lint"] 79 | dependencies = [ 80 | "colorama; platform_system == \"Windows\"", 81 | "importlib-metadata; python_version < \"3.8\"", 82 | ] 83 | files = [ 84 | {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, 85 | {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, 86 | ] 87 | 88 | [[package]] 89 | name = "colorama" 90 | version = "0.4.6" 91 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 92 | summary = "Cross-platform colored terminal text." 93 | groups = ["default", "lint", "test"] 94 | files = [ 95 | {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, 96 | {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, 97 | ] 98 | 99 | [[package]] 100 | name = "coverage" 101 | version = "6.3.3" 102 | requires_python = ">=3.7" 103 | summary = "Code coverage measurement for Python" 104 | groups = ["test"] 105 | files = [ 106 | {file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"}, 107 | {file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"}, 108 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"}, 109 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"}, 110 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"}, 111 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"}, 112 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"}, 113 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"}, 114 | {file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"}, 115 | {file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"}, 116 | {file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"}, 117 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"}, 118 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"}, 119 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"}, 120 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"}, 121 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"}, 122 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"}, 123 | {file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"}, 124 | {file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"}, 125 | {file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"}, 126 | {file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"}, 127 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"}, 128 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"}, 129 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"}, 130 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"}, 131 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"}, 132 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"}, 133 | {file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"}, 134 | {file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"}, 135 | {file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"}, 136 | {file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"}, 137 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"}, 138 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"}, 139 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"}, 140 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"}, 141 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"}, 142 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"}, 143 | {file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"}, 144 | {file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"}, 145 | {file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"}, 146 | {file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"}, 147 | ] 148 | 149 | [[package]] 150 | name = "coverage" 151 | version = "6.3.3" 152 | extras = ["toml"] 153 | requires_python = ">=3.7" 154 | summary = "Code coverage measurement for Python" 155 | groups = ["test"] 156 | dependencies = [ 157 | "coverage==6.3.3", 158 | "tomli", 159 | ] 160 | files = [ 161 | {file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"}, 162 | {file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"}, 163 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"}, 164 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"}, 165 | {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"}, 166 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"}, 167 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"}, 168 | {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"}, 169 | {file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"}, 170 | {file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"}, 171 | {file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"}, 172 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"}, 173 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"}, 174 | {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"}, 175 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"}, 176 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"}, 177 | {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"}, 178 | {file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"}, 179 | {file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"}, 180 | {file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"}, 181 | {file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"}, 182 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"}, 183 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"}, 184 | {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"}, 185 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"}, 186 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"}, 187 | {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"}, 188 | {file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"}, 189 | {file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"}, 190 | {file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"}, 191 | {file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"}, 192 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"}, 193 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"}, 194 | {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"}, 195 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"}, 196 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"}, 197 | {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"}, 198 | {file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"}, 199 | {file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"}, 200 | {file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"}, 201 | {file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"}, 202 | ] 203 | 204 | [[package]] 205 | name = "distlib" 206 | version = "0.3.7" 207 | summary = "Distribution utilities" 208 | groups = ["default"] 209 | files = [ 210 | {file = "distlib-0.3.7-py2.py3-none-any.whl", hash = "sha256:2e24928bc811348f0feb63014e97aaae3037f2cf48712d51ae61df7fd6075057"}, 211 | {file = "distlib-0.3.7.tar.gz", hash = "sha256:9dafe54b34a028eafd95039d5e5d4851a13734540f1331060d31c9916e7147a8"}, 212 | ] 213 | 214 | [[package]] 215 | name = "exceptiongroup" 216 | version = "1.0.4" 217 | requires_python = ">=3.7" 218 | summary = "Backport of PEP 654 (exception groups)" 219 | groups = ["test"] 220 | marker = "python_version < \"3.11\"" 221 | files = [ 222 | {file = "exceptiongroup-1.0.4-py3-none-any.whl", hash = "sha256:542adf9dea4055530d6e1279602fa5cb11dab2395fa650b8674eaec35fc4a828"}, 223 | {file = "exceptiongroup-1.0.4.tar.gz", hash = "sha256:bd14967b79cd9bdb54d97323216f8fdf533e278df937aa2a90089e7d6e06e5ec"}, 224 | ] 225 | 226 | [[package]] 227 | name = "filelock" 228 | version = "3.12.2" 229 | requires_python = ">=3.7" 230 | summary = "A platform independent file lock." 231 | groups = ["default"] 232 | files = [ 233 | {file = "filelock-3.12.2-py3-none-any.whl", hash = "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec"}, 234 | {file = "filelock-3.12.2.tar.gz", hash = "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81"}, 235 | ] 236 | 237 | [[package]] 238 | name = "flake8" 239 | version = "3.9.2" 240 | requires_python = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 241 | summary = "the modular source code checker: pep8 pyflakes and co" 242 | groups = ["lint"] 243 | dependencies = [ 244 | "importlib-metadata; python_version < \"3.8\"", 245 | "mccabe<0.7.0,>=0.6.0", 246 | "pycodestyle<2.8.0,>=2.7.0", 247 | "pyflakes<2.4.0,>=2.3.0", 248 | ] 249 | files = [ 250 | {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, 251 | {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, 252 | ] 253 | 254 | [[package]] 255 | name = "importlib-metadata" 256 | version = "6.7.0" 257 | requires_python = ">=3.7" 258 | summary = "Read metadata from Python packages" 259 | groups = ["default", "lint", "test"] 260 | marker = "python_version < \"3.8\"" 261 | dependencies = [ 262 | "typing-extensions>=3.6.4; python_version < \"3.8\"", 263 | "zipp>=0.5", 264 | ] 265 | files = [ 266 | {file = "importlib_metadata-6.7.0-py3-none-any.whl", hash = "sha256:cb52082e659e97afc5dac71e79de97d8681de3aa07ff18578330904a9d18e5b5"}, 267 | {file = "importlib_metadata-6.7.0.tar.gz", hash = "sha256:1aaf550d4f73e5d6783e7acb77aec43d49da8017410afae93822cc9cca98c4d4"}, 268 | ] 269 | 270 | [[package]] 271 | name = "iniconfig" 272 | version = "1.1.1" 273 | summary = "iniconfig: brain-dead simple config-ini parsing" 274 | groups = ["test"] 275 | files = [ 276 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 277 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 278 | ] 279 | 280 | [[package]] 281 | name = "mccabe" 282 | version = "0.6.1" 283 | summary = "McCabe checker, plugin for flake8" 284 | groups = ["lint"] 285 | files = [ 286 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 287 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 288 | ] 289 | 290 | [[package]] 291 | name = "mypy-extensions" 292 | version = "0.4.3" 293 | summary = "Experimental type system extensions for programs checked with the mypy typechecker." 294 | groups = ["lint"] 295 | files = [ 296 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 297 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 298 | ] 299 | 300 | [[package]] 301 | name = "packaging" 302 | version = "23.1" 303 | requires_python = ">=3.7" 304 | summary = "Core utilities for Python packages" 305 | groups = ["default", "test"] 306 | files = [ 307 | {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, 308 | {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, 309 | ] 310 | 311 | [[package]] 312 | name = "pathspec" 313 | version = "0.10.3" 314 | requires_python = ">=3.7" 315 | summary = "Utility library for gitignore style pattern matching of file paths." 316 | groups = ["lint"] 317 | files = [ 318 | {file = "pathspec-0.10.3-py3-none-any.whl", hash = "sha256:3c95343af8b756205e2aba76e843ba9520a24dd84f68c22b9f93251507509dd6"}, 319 | {file = "pathspec-0.10.3.tar.gz", hash = "sha256:56200de4077d9d0791465aa9095a01d421861e405b5096955051deefd697d6f6"}, 320 | ] 321 | 322 | [[package]] 323 | name = "platformdirs" 324 | version = "3.10.0" 325 | requires_python = ">=3.7" 326 | summary = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 327 | groups = ["default", "lint"] 328 | dependencies = [ 329 | "typing-extensions>=4.7.1; python_version < \"3.8\"", 330 | ] 331 | files = [ 332 | {file = "platformdirs-3.10.0-py3-none-any.whl", hash = "sha256:d7c24979f292f916dc9cbf8648319032f551ea8c49a4c9bf2fb556a02070ec1d"}, 333 | {file = "platformdirs-3.10.0.tar.gz", hash = "sha256:b45696dab2d7cc691a3226759c0d3b00c47c8b6e293d96f6436f733303f77f6d"}, 334 | ] 335 | 336 | [[package]] 337 | name = "pluggy" 338 | version = "1.2.0" 339 | requires_python = ">=3.7" 340 | summary = "plugin and hook calling mechanisms for python" 341 | groups = ["default", "test"] 342 | dependencies = [ 343 | "importlib-metadata>=0.12; python_version < \"3.8\"", 344 | ] 345 | files = [ 346 | {file = "pluggy-1.2.0-py3-none-any.whl", hash = "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849"}, 347 | {file = "pluggy-1.2.0.tar.gz", hash = "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3"}, 348 | ] 349 | 350 | [[package]] 351 | name = "pycodestyle" 352 | version = "2.7.0" 353 | requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 354 | summary = "Python style guide checker" 355 | groups = ["lint"] 356 | files = [ 357 | {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, 358 | {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, 359 | ] 360 | 361 | [[package]] 362 | name = "pyflakes" 363 | version = "2.3.1" 364 | requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 365 | summary = "passive checker of Python programs" 366 | groups = ["lint"] 367 | files = [ 368 | {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, 369 | {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, 370 | ] 371 | 372 | [[package]] 373 | name = "pyproject-api" 374 | version = "1.5.3" 375 | requires_python = ">=3.7" 376 | summary = "API to interact with the python pyproject.toml based projects" 377 | groups = ["default"] 378 | dependencies = [ 379 | "packaging>=23.1", 380 | "tomli>=2.0.1; python_version < \"3.11\"", 381 | ] 382 | files = [ 383 | {file = "pyproject_api-1.5.3-py3-none-any.whl", hash = "sha256:14cf09828670c7b08842249c1f28c8ee6581b872e893f81b62d5465bec41502f"}, 384 | {file = "pyproject_api-1.5.3.tar.gz", hash = "sha256:ffb5b2d7cad43f5b2688ab490de7c4d3f6f15e0b819cb588c4b771567c9729eb"}, 385 | ] 386 | 387 | [[package]] 388 | name = "pytest" 389 | version = "7.2.0" 390 | requires_python = ">=3.7" 391 | summary = "pytest: simple powerful testing with Python" 392 | groups = ["test"] 393 | dependencies = [ 394 | "attrs>=19.2.0", 395 | "colorama; sys_platform == \"win32\"", 396 | "exceptiongroup>=1.0.0rc8; python_version < \"3.11\"", 397 | "importlib-metadata>=0.12; python_version < \"3.8\"", 398 | "iniconfig", 399 | "packaging", 400 | "pluggy<2.0,>=0.12", 401 | "tomli>=1.0.0; python_version < \"3.11\"", 402 | ] 403 | files = [ 404 | {file = "pytest-7.2.0-py3-none-any.whl", hash = "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71"}, 405 | {file = "pytest-7.2.0.tar.gz", hash = "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59"}, 406 | ] 407 | 408 | [[package]] 409 | name = "tomli" 410 | version = "2.0.1" 411 | requires_python = ">=3.7" 412 | summary = "A lil' TOML parser" 413 | groups = ["default", "lint", "test"] 414 | files = [ 415 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 416 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 417 | ] 418 | 419 | [[package]] 420 | name = "tox" 421 | version = "4.8.0" 422 | requires_python = ">=3.7" 423 | summary = "tox is a generic virtualenv management and test command line tool" 424 | groups = ["default"] 425 | dependencies = [ 426 | "cachetools>=5.3.1", 427 | "chardet>=5.1", 428 | "colorama>=0.4.6", 429 | "filelock>=3.12.2", 430 | "importlib-metadata>=6.7; python_version < \"3.8\"", 431 | "packaging>=23.1", 432 | "platformdirs>=3.9.1", 433 | "pluggy>=1.2", 434 | "pyproject-api>=1.5.3", 435 | "tomli>=2.0.1; python_version < \"3.11\"", 436 | "typing-extensions>=4.7.1; python_version < \"3.8\"", 437 | "virtualenv>=20.24.1", 438 | ] 439 | files = [ 440 | {file = "tox-4.8.0-py3-none-any.whl", hash = "sha256:4991305a56983d750a0d848a34242be290452aa88d248f1bf976e4036ee8b213"}, 441 | {file = "tox-4.8.0.tar.gz", hash = "sha256:2adacf435b12ccf10b9dfa9975d8ec0afd7cbae44d300463140d2117b968037b"}, 442 | ] 443 | 444 | [[package]] 445 | name = "typed-ast" 446 | version = "1.5.4" 447 | requires_python = ">=3.6" 448 | summary = "a fork of Python 2 and 3 ast modules with type comment support" 449 | groups = ["lint"] 450 | marker = "python_version < \"3.8\" and implementation_name == \"cpython\"" 451 | files = [ 452 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, 453 | {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, 454 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, 455 | {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, 456 | {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, 457 | {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, 458 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, 459 | {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, 460 | {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, 461 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, 462 | {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, 463 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, 464 | {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, 465 | {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, 466 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, 467 | {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, 468 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, 469 | {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, 470 | {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, 471 | {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, 472 | ] 473 | 474 | [[package]] 475 | name = "typing-extensions" 476 | version = "4.7.1" 477 | requires_python = ">=3.7" 478 | summary = "Backported and Experimental Type Hints for Python 3.7+" 479 | groups = ["default", "lint", "test"] 480 | marker = "python_version < \"3.10\"" 481 | files = [ 482 | {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, 483 | {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, 484 | ] 485 | 486 | [[package]] 487 | name = "virtualenv" 488 | version = "20.24.4" 489 | requires_python = ">=3.7" 490 | summary = "Virtual Python Environment builder" 491 | groups = ["default"] 492 | dependencies = [ 493 | "distlib<1,>=0.3.7", 494 | "filelock<4,>=3.12.2", 495 | "importlib-metadata>=6.6; python_version < \"3.8\"", 496 | "platformdirs<4,>=3.9.1", 497 | ] 498 | files = [ 499 | {file = "virtualenv-20.24.4-py3-none-any.whl", hash = "sha256:29c70bb9b88510f6414ac3e55c8b413a1f96239b6b789ca123437d5e892190cb"}, 500 | {file = "virtualenv-20.24.4.tar.gz", hash = "sha256:772b05bfda7ed3b8ecd16021ca9716273ad9f4467c801f27e83ac73430246dca"}, 501 | ] 502 | 503 | [[package]] 504 | name = "zipp" 505 | version = "3.11.0" 506 | requires_python = ">=3.7" 507 | summary = "Backport of pathlib-compatible object wrapper for zip files" 508 | groups = ["default", "lint", "test"] 509 | marker = "python_version < \"3.8\"" 510 | files = [ 511 | {file = "zipp-3.11.0-py3-none-any.whl", hash = "sha256:83a28fcb75844b5c0cdaf5aa4003c2d728c77e05f5aeabe8e95e56727005fbaa"}, 512 | {file = "zipp-3.11.0.tar.gz", hash = "sha256:a7a22e05929290a67401440b39690ae6563279bced5f314609d9d03798f56766"}, 513 | ] 514 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "tox-pdm" 3 | description = "A plugin for tox that utilizes PDM as the package manager and installer" 4 | authors = [ 5 | {name = "Frost Ming", email = "me@frostming.com"}, 6 | ] 7 | dependencies = [ 8 | "tox>=4.0", 9 | "tomli; python_version<'3.11'", 10 | ] 11 | requires-python = ">=3.7" 12 | dynamic = ["version"] 13 | license = {text = "MIT"} 14 | readme = "README.md" 15 | keywords = ["tox", "testing", "pdm"] 16 | classifiers = [ 17 | "Development Status :: 4 - Beta", 18 | "Intended Audience :: Developers", 19 | "Programming Language :: Python :: 3", 20 | "Programming Language :: Python :: 3.7", 21 | "Programming Language :: Python :: 3.8", 22 | "Programming Language :: Python :: 3.9", 23 | "Programming Language :: Python :: 3.10", 24 | "Programming Language :: Python :: 3.11", 25 | ] 26 | 27 | [project.urls] 28 | Homepage = "https://github.com/pdm-project/tox-pdm" 29 | Repository = "https://github.com/pdm-project/tox-pdm" 30 | 31 | [project.entry-points.tox] 32 | pdm = "tox_pdm.plugin" 33 | 34 | [project.optional-dependencies] 35 | test = [ 36 | "pytest>=6.2", 37 | "coverage[toml]>=5.5,<6.4", 38 | ] 39 | lint = [ 40 | "flake8>=3.8", 41 | "black>=20.8b1", 42 | ] 43 | 44 | [build-system] 45 | requires = ["pdm-backend"] 46 | build-backend = "pdm.backend" 47 | 48 | [tool.pdm.version] 49 | source = "scm" 50 | 51 | [tool.pdm.scripts] 52 | test = "pytest -v tests/" 53 | 54 | [tool.pdm.scripts.lint] 55 | shell = """ 56 | flake8 tox_pdm 57 | isort --check tox_pdm tests 58 | black --check tox_pdm tests 59 | """ 60 | 61 | [tool.pdm.scripts.format] 62 | shell = """ 63 | isort tox_pdm tests 64 | black tox_pdm tests 65 | """ 66 | 67 | [tool.pytest.ini_options] 68 | filterwarnings = ["ignore::DeprecationWarning"] 69 | 70 | [tool.black] 71 | line-length = 88 72 | exclude = ''' 73 | /( 74 | \.eggs 75 | | \.git 76 | | \.hg 77 | | \.mypy_cache 78 | | \.tox 79 | | \.venv 80 | | _build 81 | | buck-out 82 | | build 83 | | dist 84 | )/ 85 | ''' 86 | 87 | [tool.isort] 88 | profile = "black" 89 | atomic = true 90 | skip_glob = ["*/setup.py"] 91 | filter_files = true 92 | -------------------------------------------------------------------------------- /tests/fixture-project/demo.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | print(sys.version_info) 4 | -------------------------------------------------------------------------------- /tests/fixture-project/pdm.lock: -------------------------------------------------------------------------------- 1 | # This file is @generated by PDM. 2 | # It is not intended for manual editing. 3 | 4 | [metadata] 5 | groups = ["default", "lint"] 6 | strategy = ["cross_platform", "inherit_metadata"] 7 | lock_version = "4.4.1" 8 | content_hash = "sha256:00c16d0c5ad91b70d922c95d035a30deb0a692792896c7781af922d77c9bd3b8" 9 | 10 | [[package]] 11 | name = "certifi" 12 | version = "2022.6.15" 13 | requires_python = ">=3.6" 14 | summary = "Python package for providing Mozilla's CA Bundle." 15 | groups = ["default"] 16 | files = [ 17 | {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, 18 | {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, 19 | ] 20 | 21 | [[package]] 22 | name = "charset-normalizer" 23 | version = "2.0.12" 24 | requires_python = ">=3.5.0" 25 | summary = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 26 | groups = ["default"] 27 | marker = "python_version >= \"3\"" 28 | files = [ 29 | {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, 30 | {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, 31 | ] 32 | 33 | [[package]] 34 | name = "flake8" 35 | version = "5.0.4" 36 | requires_python = ">=3.6.1" 37 | summary = "the modular source code checker: pep8 pyflakes and co" 38 | groups = ["lint"] 39 | dependencies = [ 40 | "importlib-metadata<4.3,>=1.1.0; python_version < \"3.8\"", 41 | "mccabe<0.8.0,>=0.7.0", 42 | "pycodestyle<2.10.0,>=2.9.0", 43 | "pyflakes<2.6.0,>=2.5.0", 44 | ] 45 | files = [ 46 | {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"}, 47 | {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"}, 48 | ] 49 | 50 | [[package]] 51 | name = "idna" 52 | version = "3.3" 53 | requires_python = ">=3.5" 54 | summary = "Internationalized Domain Names in Applications (IDNA)" 55 | groups = ["default"] 56 | marker = "python_version >= \"3\"" 57 | files = [ 58 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 59 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 60 | ] 61 | 62 | [[package]] 63 | name = "importlib-metadata" 64 | version = "4.2.0" 65 | requires_python = ">=3.6" 66 | summary = "Read metadata from Python packages" 67 | groups = ["lint"] 68 | marker = "python_version < \"3.8\"" 69 | dependencies = [ 70 | "typing-extensions>=3.6.4; python_version < \"3.8\"", 71 | "zipp>=0.5", 72 | ] 73 | files = [ 74 | {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, 75 | {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, 76 | ] 77 | 78 | [[package]] 79 | name = "mccabe" 80 | version = "0.7.0" 81 | requires_python = ">=3.6" 82 | summary = "McCabe checker, plugin for flake8" 83 | groups = ["lint"] 84 | files = [ 85 | {file = "mccabe-0.7.0-py2.py3-none-any.whl", hash = "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e"}, 86 | {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, 87 | ] 88 | 89 | [[package]] 90 | name = "pycodestyle" 91 | version = "2.9.1" 92 | requires_python = ">=3.6" 93 | summary = "Python style guide checker" 94 | groups = ["lint"] 95 | files = [ 96 | {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"}, 97 | {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"}, 98 | ] 99 | 100 | [[package]] 101 | name = "pyflakes" 102 | version = "2.5.0" 103 | requires_python = ">=3.6" 104 | summary = "passive checker of Python programs" 105 | groups = ["lint"] 106 | files = [ 107 | {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"}, 108 | {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"}, 109 | ] 110 | 111 | [[package]] 112 | name = "requests" 113 | version = "2.27.1" 114 | requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 115 | summary = "Python HTTP for Humans." 116 | groups = ["default"] 117 | dependencies = [ 118 | "certifi>=2017.4.17", 119 | "charset-normalizer~=2.0.0; python_version >= \"3\"", 120 | "idna<4,>=2.5; python_version >= \"3\"", 121 | "urllib3<1.27,>=1.21.1", 122 | ] 123 | files = [ 124 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 125 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 126 | ] 127 | 128 | [[package]] 129 | name = "typing-extensions" 130 | version = "4.7.1" 131 | requires_python = ">=3.7" 132 | summary = "Backported and Experimental Type Hints for Python 3.7+" 133 | groups = ["lint"] 134 | marker = "python_version < \"3.8\"" 135 | files = [ 136 | {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, 137 | {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, 138 | ] 139 | 140 | [[package]] 141 | name = "urllib3" 142 | version = "1.26.10" 143 | requires_python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" 144 | summary = "HTTP library with thread-safe connection pooling, file post, and more." 145 | groups = ["default"] 146 | files = [ 147 | {file = "urllib3-1.26.10-py2.py3-none-any.whl", hash = "sha256:8298d6d56d39be0e3bc13c1c97d133f9b45d797169a0e11cdd0e0489d786f7ec"}, 148 | {file = "urllib3-1.26.10.tar.gz", hash = "sha256:879ba4d1e89654d9769ce13121e0f94310ea32e8d2f8cf587b77c08bbcdb30d6"}, 149 | ] 150 | 151 | [[package]] 152 | name = "zipp" 153 | version = "3.15.0" 154 | requires_python = ">=3.7" 155 | summary = "Backport of pathlib-compatible object wrapper for zip files" 156 | groups = ["lint"] 157 | marker = "python_version < \"3.8\"" 158 | files = [ 159 | {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, 160 | {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, 161 | ] 162 | -------------------------------------------------------------------------------- /tests/fixture-project/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "demo" 3 | version = "0.1.0" 4 | description = "" 5 | authors = [ 6 | {name = "Frost Ming", email = "mianghong@gmail.com"}, 7 | ] 8 | dependencies = [ 9 | "requests~=2.25", 10 | ] 11 | requires-python = ">=3.7" 12 | license = {text = "MIT"} 13 | 14 | [project.optional-dependencies] 15 | lint = [ 16 | "flake8>=4", 17 | ] 18 | [build-system] 19 | requires = ["pdm-backend"] 20 | build-backend = "pdm.backend" 21 | 22 | [tool] 23 | [tool.pdm] 24 | [tool.pdm.scripts] 25 | lint = "flake8 demo.py" 26 | lint-shell = {shell = "flake8 demo.py"} 27 | -------------------------------------------------------------------------------- /tests/test_integration.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import sys 3 | import textwrap 4 | from pathlib import Path 5 | 6 | import pytest 7 | 8 | FIX_PROJECT = Path(__file__).with_name("fixture-project") 9 | 10 | 11 | @pytest.fixture(autouse=True) 12 | def clean_env(monkeypatch): 13 | monkeypatch.delenv("TOX_ENV_NAME", raising=False) 14 | monkeypatch.delenv("TOX_WORK_DIR", raising=False) 15 | monkeypatch.delenv("TOX_ENV_DIR", raising=False) 16 | 17 | 18 | def setup_project(tmpdir, tox_config): 19 | for filename in ("demo.py", "pdm.lock", "pyproject.toml"): 20 | shutil.copy(FIX_PROJECT / filename, tmpdir) 21 | with tmpdir.join("tox.ini").open("w", ensure=True) as f: 22 | f.write(tox_config) 23 | with tmpdir.join(".pdm.toml").open("w", ensure=True) as f: 24 | f.write( 25 | """[python] 26 | path = "{}" 27 | """.format( 28 | sys.executable.replace("\\", "/") 29 | ) 30 | ) 31 | 32 | 33 | def execute_config(tmpdir, config: str): 34 | __tracebackhide__ = True 35 | from tox.run import run as main 36 | 37 | setup_project(tmpdir, textwrap.dedent(config)) 38 | 39 | code = -1 40 | with tmpdir.as_cwd(): 41 | try: 42 | main([]) 43 | except SystemExit as e: 44 | print("e", e) 45 | code = e.code 46 | if code != 0: 47 | pytest.fail(f"non-zero exit code: {code}") 48 | 49 | package = tmpdir.join(".tox/.pkg/dist/demo-0.1.0.tar.gz") 50 | assert package.exists() 51 | 52 | 53 | def test_install_conditional_deps(tmpdir): 54 | execute_config( 55 | tmpdir, 56 | """ 57 | [tox] 58 | envlist = django{3,4} 59 | passenv = LD_PRELOAD 60 | isolated_build = True 61 | 62 | [testenv] 63 | groups = 64 | lint 65 | deps = 66 | django3: Django~=3.0 67 | django4: Django~=4.0 68 | commands = 69 | django-admin --version 70 | flake8 --version 71 | """, 72 | ) 73 | 74 | 75 | def test_use_pdm_scripts(tmpdir): 76 | execute_config( 77 | tmpdir, 78 | """ 79 | [tox] 80 | envlist = py3 81 | passenv = LD_PRELOAD 82 | isolated_build = True 83 | 84 | [testenv] 85 | groups = lint 86 | commands = lint 87 | """, 88 | ) 89 | 90 | 91 | def test_use_pdm_shell_scripts(tmpdir): 92 | execute_config( 93 | tmpdir, 94 | """ 95 | [tox] 96 | envlist = py3 97 | passenv = LD_PRELOAD 98 | isolated_build = True 99 | 100 | [testenv] 101 | groups = lint 102 | commands = lint-shell 103 | """, 104 | ) 105 | 106 | 107 | def test_pdm_install_not_sync(tmpdir): 108 | execute_config( 109 | tmpdir, 110 | """ 111 | [tox] 112 | envlist = py3 113 | passenv = LD_PRELOAD 114 | isolated_build = True 115 | 116 | [testenv] 117 | groups = lint 118 | pdm_sync = False 119 | commands = flake8 --version 120 | """, 121 | ) 122 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = 3 | E203 4 | W503 5 | max-line-length = 88 6 | 7 | [tox] 8 | envlist = py3{7,8,9,10,11}, lint 9 | passenv = LD_PRELOAD 10 | 11 | [testenv] 12 | groups = test 13 | commands = 14 | tox --version 15 | test {posargs} 16 | 17 | [testenv:lint] 18 | groups = lint 19 | skip_install = true 20 | commands = lint 21 | -------------------------------------------------------------------------------- /tox_pdm/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pdm-project/tox-pdm/8471ad4ac6499e8d470d36cc15150cdea75af6c8/tox_pdm/__init__.py -------------------------------------------------------------------------------- /tox_pdm/plugin.py: -------------------------------------------------------------------------------- 1 | """Plugin specification for Tox 4""" 2 | 3 | from __future__ import annotations 4 | 5 | import os 6 | import typing as t 7 | from pathlib import Path 8 | 9 | from packaging.requirements import Requirement 10 | from tox.config.set_env import SetEnv 11 | from tox.config.sets import EnvConfigSet 12 | from tox.execute.request import StdinSource 13 | from tox.plugin import impl 14 | from tox.tox_env.python.virtual_env.package.pyproject import Pep517VirtualEnvPackager 15 | from tox.tox_env.python.virtual_env.runner import VirtualEnvRunner 16 | 17 | from .utils import pdm_scripts 18 | 19 | if t.TYPE_CHECKING: 20 | from argparse import ArgumentParser 21 | 22 | from tox.execute.api import Execute, Outcome 23 | from tox.tox_env.register import ToxEnvRegister 24 | 25 | os.environ.pop("NO_SITE_PACKAGES", None) 26 | 27 | 28 | @impl 29 | def tox_add_option(parser: ArgumentParser) -> None: 30 | parser.add_argument("--pdm", default="pdm", help="The executable path of PDM") 31 | 32 | 33 | @impl 34 | def tox_register_tox_env(register: ToxEnvRegister) -> t.Optional[bool]: 35 | register.add_run_env(PdmRunner) 36 | register.add_package_env(PdmPep517Packager) 37 | register.default_env_runner = "pdm" 38 | 39 | 40 | class PdmRunner(VirtualEnvRunner): 41 | def _setup_env(self) -> None: 42 | super()._setup_env() 43 | pdm = self.options.pdm 44 | if pdm not in self.conf["allowlist_externals"]: 45 | self.conf["allowlist_externals"].append(pdm) 46 | groups = self.conf["groups"] 47 | op = "sync" if self.conf["pdm_sync"] else "install" 48 | cmd = [pdm, op, "--no-self"] 49 | if not groups: 50 | return 51 | for group in groups: 52 | cmd.extend(("--group", group)) 53 | set_env: SetEnv = self.conf["setenv"] 54 | if "VIRTUAL_ENV" not in set_env: 55 | set_env.update({"VIRTUAL_ENV": str(self.env_dir)}) 56 | outcome = self.execute(cmd, StdinSource.OFF, run_id="install_deps") 57 | outcome.assert_success() 58 | 59 | @staticmethod 60 | def _load_pass_env(pass_env: list[str]) -> dict[str, str]: 61 | env = VirtualEnvRunner._load_pass_env(pass_env) 62 | env.update({"PDM_IGNORE_SAVED_PYTHON": "1", "PDM_USE_VENV": "1"}) 63 | return env 64 | 65 | def register_config(self) -> None: 66 | super().register_config() 67 | self.conf.add_config( 68 | "groups", 69 | of_type=t.List[str], 70 | default=[], 71 | desc="Specify the dependency groups to install", 72 | ) 73 | self.conf.add_config( 74 | "pdm_sync", 75 | of_type=bool, 76 | default=True, 77 | desc="Disable to use 'pdm install' instead of 'pdm sync'.", 78 | ) 79 | 80 | @staticmethod 81 | def id() -> str: 82 | return "pdm" 83 | 84 | @property 85 | def _package_tox_env_type(self) -> str: 86 | return "pdm-pep-517" 87 | 88 | def execute( 89 | self, 90 | cmd: t.Sequence[Path | str], 91 | stdin: StdinSource, 92 | show: bool | None = None, 93 | cwd: Path | None = None, 94 | run_id: str = "", 95 | executor: Execute | None = None, 96 | ) -> Outcome: 97 | scripts = pdm_scripts(self.core["tox_root"]) 98 | if scripts and cmd[0] in scripts: 99 | cmd = ["pdm", "run", *cmd] 100 | return super().execute(cmd, stdin, show, cwd, run_id, executor) 101 | 102 | 103 | class PdmPep517Packager(Pep517VirtualEnvPackager): 104 | """A subclass of Pep517VirtualEnvPackager that doesn't 105 | analyze package dependencies, since they will be installed by PDM. 106 | """ 107 | 108 | @staticmethod 109 | def id() -> str: 110 | return "pdm-pep-517" 111 | 112 | def _load_deps(self, for_env: EnvConfigSet) -> list[Requirement]: 113 | return [] 114 | -------------------------------------------------------------------------------- /tox_pdm/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | 3 | import sys 4 | from pathlib import Path 5 | from typing import Any 6 | 7 | if sys.version_info >= (3, 11): 8 | import tomllib 9 | else: 10 | import tomli as tomllib 11 | 12 | 13 | def pdm_scripts(root: Path) -> dict[str, Any]: 14 | pyproject_toml = root / "pyproject.toml" 15 | if pyproject_toml.exists(): 16 | with open(pyproject_toml, "rb") as f: 17 | pyproject = tomllib.load(f) 18 | return pyproject.get("tool", {}).get("pdm", {}).get("scripts", {}) 19 | return {} 20 | --------------------------------------------------------------------------------