pax_global_header00006660000000000000000000000064144320514100014504gustar00rootroot0000000000000052 comment=6c9301b1885e791ff37443145af01d5e27737429 requirements-detector-1.2.2/000077500000000000000000000000001443205141000160405ustar00rootroot00000000000000requirements-detector-1.2.2/.coveragerc000066400000000000000000000000441443205141000201570ustar00rootroot00000000000000 [run] source=requirements_detector requirements-detector-1.2.2/.github/000077500000000000000000000000001443205141000174005ustar00rootroot00000000000000requirements-detector-1.2.2/.github/workflows/000077500000000000000000000000001443205141000214355ustar00rootroot00000000000000requirements-detector-1.2.2/.github/workflows/ci.yaml000066400000000000000000000157331443205141000227250ustar00rootroot00000000000000name: CI on: [push, pull_request] env: CACHE_VERSION: 3 DEFAULT_PYTHON: 3.9 PRE_COMMIT_CACHE: ~/.cache/pre-commit jobs: prepare-base: name: Prepare base dependencies runs-on: ubuntu-latest outputs: python-key: ${{ steps.generate-python-key.outputs.key }} pre-commit-key: ${{ steps.generate-pre-commit-key.outputs.key }} steps: - name: Check out code from GitHub uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v4 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Generate partial Python venv restore key id: generate-python-key run: >- echo "key=base-venv-${{ env.CACHE_VERSION }}-${{ hashFiles('setup.cfg') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv uses: actions/cache@v3 with: path: venv key: >- ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ steps.generate-python-key.outputs.key }} restore-keys: | ${{ runner.os }}-${{ steps.python.outputs.python-version }}-base-venv-${{ env.CACHE_VERSION }}- - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | python -m venv venv . venv/bin/activate python -m pip install --disable-pip-version-check -U pip poetry poetry install - name: Generate pre-commit restore key id: generate-pre-commit-key run: >- echo "key=pre-commit-${{ env.CACHE_VERSION }}-${{ hashFiles('.pre-commit-config.yaml') }}" >> $GITHUB_OUTPUT - name: Restore pre-commit environment id: cache-precommit uses: actions/cache@v3 with: path: ${{ env.PRE_COMMIT_CACHE }} key: >- ${{ runner.os }}-${{ steps.generate-pre-commit-key.outputs.key }} restore-keys: | ${{ runner.os }}-pre-commit-${{ env.CACHE_VERSION }}- - name: Install pre-commit dependencies if: steps.cache-precommit.outputs.cache-hit != 'true' run: | . venv/bin/activate pre-commit install --install-hooks formatting: name: Run pre-commit checks runs-on: ubuntu-latest needs: prepare-base steps: - name: Check out code from GitHub uses: actions/checkout@v3 - name: Set up Python ${{ env.DEFAULT_PYTHON }} id: python uses: actions/setup-python@v4 with: python-version: ${{ env.DEFAULT_PYTHON }} - name: Restore Python virtual environment id: cache-venv uses: actions/cache@v3 with: path: venv key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ needs.prepare-base.outputs.python-key }} - name: Fail job if Python cache restore failed if: steps.cache-venv.outputs.cache-hit != 'true' run: | echo "Failed to restore Python venv from cache" exit 1 - name: Restore pre-commit environment id: cache-precommit uses: actions/cache@v3 with: path: ${{ env.PRE_COMMIT_CACHE }} key: ${{ runner.os }}-${{ needs.prepare-base.outputs.pre-commit-key }} - name: Fail job if pre-commit cache restore failed if: steps.cache-precommit.outputs.cache-hit != 'true' run: | echo "Failed to restore pre-commit environment from cache" exit 1 - name: Run formatting check run: | . venv/bin/activate python -m pip install --disable-pip-version-check -U pip poetry poetry install pre-commit run --all-files prepare-tests-linux: name: Prepare tests for Python ${{ matrix.python-version }} (Linux) runs-on: ubuntu-latest strategy: matrix: python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] outputs: python-key: ${{ steps.generate-python-key.outputs.key }} steps: - name: Check out code from GitHub uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Python ${{ matrix.python-version }} id: python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Generate partial Python venv restore key id: generate-python-key run: >- echo "key=venv-${{ env.CACHE_VERSION }}-${{ hashFiles('setup.cfg') }}" >> $GITHUB_OUTPUT - name: Restore Python virtual environment id: cache-venv uses: actions/cache@v3 with: path: venv key: >- ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ steps.generate-python-key.outputs.key }} restore-keys: | ${{ runner.os }}-${{ steps.python.outputs.python-version }}-venv-${{ env.CACHE_VERSION }}- - name: Create Python virtual environment if: steps.cache-venv.outputs.cache-hit != 'true' run: | python -m venv venv . venv/bin/activate python -m pip install --disable-pip-version-check -U pip poetry poetry install test-linux: name: Run tests Python ${{ matrix.python-version }} (Linux) runs-on: ubuntu-latest needs: prepare-tests-linux strategy: fail-fast: false matrix: python-version: [3.7, 3.8, 3.9, "3.10", "3.11"] steps: - name: Check out code from GitHub uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} id: python uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Restore Python virtual environment id: cache-venv uses: actions/cache@v3 with: path: venv key: ${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ needs.prepare-tests-linux.outputs.python-key }} - name: Fail job if Python cache restore failed if: steps.cache-venv.outputs.cache-hit != 'true' run: | echo "Failed to restore Python venv from cache" exit 1 - name: Run tests run: | . venv/bin/activate python -m pip install --disable-pip-version-check -U pip poetry poetry install pytest -s --benchmark-disable --cov --cov-report= - name: Check it can actually run run: | . venv/bin/activate python -m pip install --disable-pip-version-check -U pip poetry poetry install detect-requirements - name: Build and validate packages run: | . venv/bin/activate echo "Building packages" rm -rf dist/ poetry build echo "Validating packages" twine check dist/* echo "Packages build and valdiate successfully." requirements-detector-1.2.2/.gitignore000066400000000000000000000005171443205141000200330ustar00rootroot00000000000000*.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml # Translations *.mo # Mr Developer .mr.developer.cfg .project .pydevproject .benchmarks .venv env.txt .idea requirements-detector-1.2.2/.landscape.yaml000066400000000000000000000000731443205141000207340ustar00rootroot00000000000000doc-warnings: no strictness: veryhigh max-line-length: 120 requirements-detector-1.2.2/.pre-commit-config.yaml000066400000000000000000000010721443205141000223210ustar00rootroot00000000000000fail_fast: true repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.4.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer - id: mixed-line-ending args: [ --fix=lf ] - id: check-added-large-files - repo: https://github.com/psf/black rev: 23.3.0 hooks: - id: black args: - --force-exclude=tests/ - --line-length=120 - repo: https://github.com/PyCQA/isort rev: 5.12.0 hooks: - id: isort args: ["--profile", "black", "--filter-files"] requirements-detector-1.2.2/LICENSE000066400000000000000000000020201443205141000170370ustar00rootroot00000000000000The MIT License Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. requirements-detector-1.2.2/README.md000066400000000000000000000050211443205141000173150ustar00rootroot00000000000000# Requirements Detector ## Status [![Latest Version](https://img.shields.io/pypi/v/requirements-detector.svg?label=version&style=flat)](https://pypi.python.org/pypi/requirements-detector) [![Build Satus](https://github.com/landscapeio/requirements-detector/actions/workflows/ci.yaml/badge.svg)](https://github.com/landscapeio/requirements-detector/actions/workflows/ci.yaml) [![Health](https://landscape.io/github/landscapeio/requirements-detector/master/landscape.svg?style=flat)](https://landscape.io/github/landscapeio/requirements-detector/master) [![Coverage Status](https://img.shields.io/coveralls/landscapeio/requirements-detector.svg?style=flat)](https://coveralls.io/r/landscapeio/requirements-detector) [![Documentation](https://readthedocs.org/projects/requirements-detector/badge/?version=master)](https://readthedocs.org/projects/requirements-detector/) ## About `requirements-detector` is a simple Python tool which attempts to find and list the requirements of a Python project. When run from the root of a Python project, it will try to ascertain which libraries and the versions of those libraries that the project depends on. It uses the following methods in order, in the root of the project: 1. Parse `setup.py` (if this is successful, the remaining steps are skipped) 2. Parse `pyproject.yoml` (if a `tool.poetry.dependencies` section is found, the remaining steps are skipped) 3. Parse `requirements.txt` or `requirements.pip` 4. Parse all `*.txt` and `*.pip` files inside a folder called `requirements` 5. Parse all files in the root folder matching `*requirements*.txt` or `reqs.txt` (so for example, `pip_requirements.txt` would match, as would `requirements_common.txt`) ### Usage ``` detect-requirements [path] ``` If `path` is not specified, the current working directory will be used. ### Output The output will be plaintext, and match that of a [pip requirements file](http://www.pip-installer.org/en/latest/logic.html), for example: ``` Django==1.5.2 South>=0.8 anyjson celery>=2.2,<3 ``` ### Usage From Python ``` >>> import os >>> from requirements_detector import find_requirements >>> find_requirements(os.getcwd()) [DetectedRequirement:Django==1.5.2, DetectedRequirement:South>=0.8, ...] ``` If you know the relevant file or directory, you can use `from_requirements_txt`, `from_setup_py` or `from_requirements_dir` directly. ``` >>> from requirements_detector import from_requirements_txt >>> from_requirements_txt("/path/to/requirements.txt") [DetectedRequirement:Django==1.5.2, DetectedRequirement:South>=0.8, ...] ``` requirements-detector-1.2.2/poetry.lock000066400000000000000000002720711443205141000202450ustar00rootroot00000000000000# This file is automatically @generated by Poetry and should not be changed by hand. [[package]] name = "astroid" version = "2.11.7" description = "An abstract syntax tree for Python with inference support." category = "main" optional = false python-versions = ">=3.6.2" files = [ {file = "astroid-2.11.7-py3-none-any.whl", hash = "sha256:86b0a340a512c65abf4368b80252754cda17c02cdbbd3f587dddf98112233e7b"}, {file = "astroid-2.11.7.tar.gz", hash = "sha256:bb24615c77f4837c707669d16907331374ae8a964650a66999da3f5ca68dc946"}, ] [package.dependencies] lazy-object-proxy = ">=1.4.0" setuptools = ">=20.0" typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} wrapt = ">=1.11,<2" [[package]] name = "atomicwrites" version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, ] [[package]] name = "attrs" version = "23.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "attrs-23.1.0-py3-none-any.whl", hash = "sha256:1f28b4522cdc2fb4256ac1a020c78acf9cba2c6b461ccd2c126f3aa8e8335d04"}, {file = "attrs-23.1.0.tar.gz", hash = "sha256:6279836d581513a26f1bf235f9acd333bc9115683f14f7e8fae46c98fc50e015"}, ] [package.dependencies] importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [package.extras] cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] dev = ["attrs[docs,tests]", "pre-commit"] docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] tests = ["attrs[tests-no-zope]", "zope-interface"] tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] [[package]] name = "bleach" version = "6.0.0" description = "An easy safelist-based HTML-sanitizing tool." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "bleach-6.0.0-py3-none-any.whl", hash = "sha256:33c16e3353dbd13028ab4799a0f89a83f113405c766e9c122df8a06f5b85b3f4"}, {file = "bleach-6.0.0.tar.gz", hash = "sha256:1a1a85c1595e07d8db14c5f09f09e6433502c51c595970edc090551f0db99414"}, ] [package.dependencies] six = ">=1.9.0" webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.2)"] [[package]] name = "certifi" version = "2022.12.7" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, ] [[package]] name = "cffi" version = "1.15.1" description = "Foreign Function Interface for Python calling C code." category = "dev" optional = false python-versions = "*" files = [ {file = "cffi-1.15.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a66d3508133af6e8548451b25058d5812812ec3798c886bf38ed24a98216fab2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:470c103ae716238bbe698d67ad020e1db9d9dba34fa5a899b5e21577e6d52ed2"}, {file = "cffi-1.15.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:9ad5db27f9cabae298d151c85cf2bad1d359a1b9c686a275df03385758e2f914"}, {file = "cffi-1.15.1-cp27-cp27m-win32.whl", hash = "sha256:b3bbeb01c2b273cca1e1e0c5df57f12dce9a4dd331b4fa1635b8bec26350bde3"}, {file = "cffi-1.15.1-cp27-cp27m-win_amd64.whl", hash = "sha256:e00b098126fd45523dd056d2efba6c5a63b71ffe9f2bbe1a4fe1716e1d0c331e"}, {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:d61f4695e6c866a23a21acab0509af1cdfd2c013cf256bbf5b6b5e2695827162"}, {file = "cffi-1.15.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ed9cb427ba5504c1dc15ede7d516b84757c3e3d7868ccc85121d9310d27eed0b"}, {file = "cffi-1.15.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:39d39875251ca8f612b6f33e6b1195af86d1b3e60086068be9cc053aa4376e21"}, {file = "cffi-1.15.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:285d29981935eb726a4399badae8f0ffdff4f5050eaa6d0cfc3f64b857b77185"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3eb6971dcff08619f8d91607cfc726518b6fa2a9eba42856be181c6d0d9515fd"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:21157295583fe8943475029ed5abdcf71eb3911894724e360acff1d61c1d54bc"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5635bd9cb9731e6d4a1132a498dd34f764034a8ce60cef4f5319c0541159392f"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2012c72d854c2d03e45d06ae57f40d78e5770d252f195b93f581acf3ba44496e"}, {file = "cffi-1.15.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd86c085fae2efd48ac91dd7ccffcfc0571387fe1193d33b6394db7ef31fe2a4"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:fa6693661a4c91757f4412306191b6dc88c1703f780c8234035eac011922bc01"}, {file = "cffi-1.15.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59c0b02d0a6c384d453fece7566d1c7e6b7bae4fc5874ef2ef46d56776d61c9e"}, {file = "cffi-1.15.1-cp310-cp310-win32.whl", hash = "sha256:cba9d6b9a7d64d4bd46167096fc9d2f835e25d7e4c121fb2ddfc6528fb0413b2"}, {file = "cffi-1.15.1-cp310-cp310-win_amd64.whl", hash = "sha256:ce4bcc037df4fc5e3d184794f27bdaab018943698f4ca31630bc7f84a7b69c6d"}, {file = "cffi-1.15.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3d08afd128ddaa624a48cf2b859afef385b720bb4b43df214f85616922e6a5ac"}, {file = "cffi-1.15.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3799aecf2e17cf585d977b780ce79ff0dc9b78d799fc694221ce814c2c19db83"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a591fe9e525846e4d154205572a029f653ada1a78b93697f3b5a8f1f2bc055b9"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3548db281cd7d2561c9ad9984681c95f7b0e38881201e157833a2342c30d5e8c"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91fc98adde3d7881af9b59ed0294046f3806221863722ba7d8d120c575314325"}, {file = "cffi-1.15.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94411f22c3985acaec6f83c6df553f2dbe17b698cc7f8ae751ff2237d96b9e3c"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:03425bdae262c76aad70202debd780501fabeaca237cdfddc008987c0e0f59ef"}, {file = "cffi-1.15.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cc4d65aeeaa04136a12677d3dd0b1c0c94dc43abac5860ab33cceb42b801c1e8"}, {file = "cffi-1.15.1-cp311-cp311-win32.whl", hash = "sha256:a0f100c8912c114ff53e1202d0078b425bee3649ae34d7b070e9697f93c5d52d"}, {file = "cffi-1.15.1-cp311-cp311-win_amd64.whl", hash = "sha256:04ed324bda3cda42b9b695d51bb7d54b680b9719cfab04227cdd1e04e5de3104"}, {file = "cffi-1.15.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50a74364d85fd319352182ef59c5c790484a336f6db772c1a9231f1c3ed0cbd7"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e263d77ee3dd201c3a142934a086a4450861778baaeeb45db4591ef65550b0a6"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cec7d9412a9102bdc577382c3929b337320c4c4c4849f2c5cdd14d7368c5562d"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4289fc34b2f5316fbb762d75362931e351941fa95fa18789191b33fc4cf9504a"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:173379135477dc8cac4bc58f45db08ab45d228b3363adb7af79436135d028405"}, {file = "cffi-1.15.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6975a3fac6bc83c4a65c9f9fcab9e47019a11d3d2cf7f3c0d03431bf145a941e"}, {file = "cffi-1.15.1-cp36-cp36m-win32.whl", hash = "sha256:2470043b93ff09bf8fb1d46d1cb756ce6132c54826661a32d4e4d132e1977adf"}, {file = "cffi-1.15.1-cp36-cp36m-win_amd64.whl", hash = "sha256:30d78fbc8ebf9c92c9b7823ee18eb92f2e6ef79b45ac84db507f52fbe3ec4497"}, {file = "cffi-1.15.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:198caafb44239b60e252492445da556afafc7d1e3ab7a1fb3f0584ef6d742375"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ef34d190326c3b1f822a5b7a45f6c4535e2f47ed06fec77d3d799c450b2651e"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8102eaf27e1e448db915d08afa8b41d6c7ca7a04b7d73af6514df10a3e74bd82"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5df2768244d19ab7f60546d0c7c63ce1581f7af8b5de3eb3004b9b6fc8a9f84b"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8c4917bd7ad33e8eb21e9a5bbba979b49d9a97acb3a803092cbc1133e20343c"}, {file = "cffi-1.15.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e2642fe3142e4cc4af0799748233ad6da94c62a8bec3a6648bf8ee68b1c7426"}, {file = "cffi-1.15.1-cp37-cp37m-win32.whl", hash = "sha256:e229a521186c75c8ad9490854fd8bbdd9a0c9aa3a524326b55be83b54d4e0ad9"}, {file = "cffi-1.15.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a0b71b1b8fbf2b96e41c4d990244165e2c9be83d54962a9a1d118fd8657d2045"}, {file = "cffi-1.15.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:320dab6e7cb2eacdf0e658569d2575c4dad258c0fcc794f46215e1e39f90f2c3"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e74c6b51a9ed6589199c787bf5f9875612ca4a8a0785fb2d4a84429badaf22a"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5c84c68147988265e60416b57fc83425a78058853509c1b0629c180094904a5"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b926aa83d1edb5aa5b427b4053dc420ec295a08e40911296b9eb1b6170f6cca"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:87c450779d0914f2861b8526e035c5e6da0a3199d8f1add1a665e1cbc6fc6d02"}, {file = "cffi-1.15.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f2c9f67e9821cad2e5f480bc8d83b8742896f1242dba247911072d4fa94c192"}, {file = "cffi-1.15.1-cp38-cp38-win32.whl", hash = "sha256:8b7ee99e510d7b66cdb6c593f21c043c248537a32e0bedf02e01e9553a172314"}, {file = "cffi-1.15.1-cp38-cp38-win_amd64.whl", hash = "sha256:00a9ed42e88df81ffae7a8ab6d9356b371399b91dbdf0c3cb1e84c03a13aceb5"}, {file = "cffi-1.15.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:54a2db7b78338edd780e7ef7f9f6c442500fb0d41a5a4ea24fff1c929d5af585"}, {file = "cffi-1.15.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fcd131dd944808b5bdb38e6f5b53013c5aa4f334c5cad0c72742f6eba4b73db0"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7473e861101c9e72452f9bf8acb984947aa1661a7704553a9f6e4baa5ba64415"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c9a799e985904922a4d207a94eae35c78ebae90e128f0c4e521ce339396be9d"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3bcde07039e586f91b45c88f8583ea7cf7a0770df3a1649627bf598332cb6984"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33ab79603146aace82c2427da5ca6e58f2b3f2fb5da893ceac0c42218a40be35"}, {file = "cffi-1.15.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d598b938678ebf3c67377cdd45e09d431369c3b1a5b331058c338e201f12b27"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db0fbb9c62743ce59a9ff687eb5f4afbe77e5e8403d6697f7446e5f609976f76"}, {file = "cffi-1.15.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:98d85c6a2bef81588d9227dde12db8a7f47f639f4a17c9ae08e773aa9c697bf3"}, {file = "cffi-1.15.1-cp39-cp39-win32.whl", hash = "sha256:40f4774f5a9d4f5e344f31a32b5096977b5d48560c5592e2f3d2c4374bd543ee"}, {file = "cffi-1.15.1-cp39-cp39-win_amd64.whl", hash = "sha256:70df4e3b545a17496c9b3f41f5115e69a4f2e77e94e1d2a8e1070bc0c38c8a3c"}, {file = "cffi-1.15.1.tar.gz", hash = "sha256:d400bfb9a37b1351253cb402671cea7e89bdecc294e8016a707f6d1d8ac934f9"}, ] [package.dependencies] pycparser = "*" [[package]] name = "cfgv" version = "3.3.1" description = "Validate configuration and produce human readable error messages." category = "dev" optional = false python-versions = ">=3.6.1" files = [ {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] [[package]] name = "charset-normalizer" version = "3.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false python-versions = ">=3.7.0" files = [ {file = "charset-normalizer-3.1.0.tar.gz", hash = "sha256:34e0a2f9c370eb95597aae63bf85eb5e96826d81e3dcf88b8886012906f509b5"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e0ac8959c929593fee38da1c2b64ee9778733cdf03c482c9ff1d508b6b593b2b"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d7fc3fca01da18fbabe4625d64bb612b533533ed10045a2ac3dd194bfa656b60"}, {file = "charset_normalizer-3.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:04eefcee095f58eaabe6dc3cc2262f3bcd776d2c67005880894f447b3f2cb9c1"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20064ead0717cf9a73a6d1e779b23d149b53daf971169289ed2ed43a71e8d3b0"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1435ae15108b1cb6fffbcea2af3d468683b7afed0169ad718451f8db5d1aff6f"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84132a54c750fda57729d1e2599bb598f5fa0344085dbde5003ba429a4798c0"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f2568b4189dda1c567339b48cba4ac7384accb9c2a7ed655cd86b04055c795"}, {file = "charset_normalizer-3.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11d3bcb7be35e7b1bba2c23beedac81ee893ac9871d0ba79effc7fc01167db6c"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:891cf9b48776b5c61c700b55a598621fdb7b1e301a550365571e9624f270c203"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5f008525e02908b20e04707a4f704cd286d94718f48bb33edddc7d7b584dddc1"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:b06f0d3bf045158d2fb8837c5785fe9ff9b8c93358be64461a1089f5da983137"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:49919f8400b5e49e961f320c735388ee686a62327e773fa5b3ce6721f7e785ce"}, {file = "charset_normalizer-3.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:22908891a380d50738e1f978667536f6c6b526a2064156203d418f4856d6e86a"}, {file = "charset_normalizer-3.1.0-cp310-cp310-win32.whl", hash = "sha256:12d1a39aa6b8c6f6248bb54550efcc1c38ce0d8096a146638fd4738e42284448"}, {file = "charset_normalizer-3.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:65ed923f84a6844de5fd29726b888e58c62820e0769b76565480e1fdc3d062f8"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9a3267620866c9d17b959a84dd0bd2d45719b817245e49371ead79ed4f710d19"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6734e606355834f13445b6adc38b53c0fd45f1a56a9ba06c2058f86893ae8017"}, {file = "charset_normalizer-3.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f8303414c7b03f794347ad062c0516cee0e15f7a612abd0ce1e25caf6ceb47df"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf53a6cebad0eae578f062c7d462155eada9c172bd8c4d250b8c1d8eb7f916a"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3dc5b6a8ecfdc5748a7e429782598e4f17ef378e3e272eeb1340ea57c9109f41"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e1b25e3ad6c909f398df8921780d6a3d120d8c09466720226fc621605b6f92b1"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ca564606d2caafb0abe6d1b5311c2649e8071eb241b2d64e75a0d0065107e62"}, {file = "charset_normalizer-3.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b82fab78e0b1329e183a65260581de4375f619167478dddab510c6c6fb04d9b6"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bd7163182133c0c7701b25e604cf1611c0d87712e56e88e7ee5d72deab3e76b5"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:11d117e6c63e8f495412d37e7dc2e2fff09c34b2d09dbe2bee3c6229577818be"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:cf6511efa4801b9b38dc5546d7547d5b5c6ef4b081c60b23e4d941d0eba9cbeb"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:abc1185d79f47c0a7aaf7e2412a0eb2c03b724581139193d2d82b3ad8cbb00ac"}, {file = "charset_normalizer-3.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:cb7b2ab0188829593b9de646545175547a70d9a6e2b63bf2cd87a0a391599324"}, {file = "charset_normalizer-3.1.0-cp311-cp311-win32.whl", hash = "sha256:c36bcbc0d5174a80d6cccf43a0ecaca44e81d25be4b7f90f0ed7bcfbb5a00909"}, {file = "charset_normalizer-3.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:cca4def576f47a09a943666b8f829606bcb17e2bc2d5911a46c8f8da45f56755"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c95f12b74681e9ae127728f7e5409cbbef9cd914d5896ef238cc779b8152373"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fca62a8301b605b954ad2e9c3666f9d97f63872aa4efcae5492baca2056b74ab"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac0aa6cd53ab9a31d397f8303f92c42f534693528fafbdb997c82bae6e477ad9"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3af8e0f07399d3176b179f2e2634c3ce9c1301379a6b8c9c9aeecd481da494f"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a5fc78f9e3f501a1614a98f7c54d3969f3ad9bba8ba3d9b438c3bc5d047dd28"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:628c985afb2c7d27a4800bfb609e03985aaecb42f955049957814e0491d4006d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:74db0052d985cf37fa111828d0dd230776ac99c740e1a758ad99094be4f1803d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e8fcdd8f672a1c4fc8d0bd3a2b576b152d2a349782d1eb0f6b8e52e9954731d"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:04afa6387e2b282cf78ff3dbce20f0cc071c12dc8f685bd40960cc68644cfea6"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:dd5653e67b149503c68c4018bf07e42eeed6b4e956b24c00ccdf93ac79cdff84"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d2686f91611f9e17f4548dbf050e75b079bbc2a82be565832bc8ea9047b61c8c"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-win32.whl", hash = "sha256:4155b51ae05ed47199dc5b2a4e62abccb274cee6b01da5b895099b61b1982974"}, {file = "charset_normalizer-3.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:322102cdf1ab682ecc7d9b1c5eed4ec59657a65e1c146a0da342b78f4112db23"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e633940f28c1e913615fd624fcdd72fdba807bf53ea6925d6a588e84e1151531"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3a06f32c9634a8705f4ca9946d667609f52cf130d5548881401f1eb2c39b1e2c"}, {file = "charset_normalizer-3.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7381c66e0561c5757ffe616af869b916c8b4e42b367ab29fedc98481d1e74e14"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3573d376454d956553c356df45bb824262c397c6e26ce43e8203c4c540ee0acb"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e89df2958e5159b811af9ff0f92614dabf4ff617c03a4c1c6ff53bf1c399e0e1"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:78cacd03e79d009d95635e7d6ff12c21eb89b894c354bd2b2ed0b4763373693b"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:de5695a6f1d8340b12a5d6d4484290ee74d61e467c39ff03b39e30df62cf83a0"}, {file = "charset_normalizer-3.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c60b9c202d00052183c9be85e5eaf18a4ada0a47d188a83c8f5c5b23252f649"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f645caaf0008bacf349875a974220f1f1da349c5dbe7c4ec93048cdc785a3326"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ea9f9c6034ea2d93d9147818f17c2a0860d41b71c38b9ce4d55f21b6f9165a11"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:80d1543d58bd3d6c271b66abf454d437a438dff01c3e62fdbcd68f2a11310d4b"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:73dc03a6a7e30b7edc5b01b601e53e7fc924b04e1835e8e407c12c037e81adbd"}, {file = "charset_normalizer-3.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6f5c2e7bc8a4bf7c426599765b1bd33217ec84023033672c1e9a8b35eaeaaaf8"}, {file = "charset_normalizer-3.1.0-cp38-cp38-win32.whl", hash = "sha256:12a2b561af122e3d94cdb97fe6fb2bb2b82cef0cdca131646fdb940a1eda04f0"}, {file = "charset_normalizer-3.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3160a0fd9754aab7d47f95a6b63ab355388d890163eb03b2d2b87ab0a30cfa59"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38e812a197bf8e71a59fe55b757a84c1f946d0ac114acafaafaf21667a7e169e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6baf0baf0d5d265fa7944feb9f7451cc316bfe30e8df1a61b1bb08577c554f31"}, {file = "charset_normalizer-3.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8f25e17ab3039b05f762b0a55ae0b3632b2e073d9c8fc88e89aca31a6198e88f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3747443b6a904001473370d7810aa19c3a180ccd52a7157aacc264a5ac79265e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b116502087ce8a6b7a5f1814568ccbd0e9f6cfd99948aa59b0e241dc57cf739f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d16fd5252f883eb074ca55cb622bc0bee49b979ae4e8639fff6ca3ff44f9f854"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fa558996782fc226b529fdd2ed7866c2c6ec91cee82735c98a197fae39f706"}, {file = "charset_normalizer-3.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f6c7a8a57e9405cad7485f4c9d3172ae486cfef1344b5ddd8e5239582d7355e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ac3775e3311661d4adace3697a52ac0bab17edd166087d493b52d4f4f553f9f0"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:10c93628d7497c81686e8e5e557aafa78f230cd9e77dd0c40032ef90c18f2230"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:6f4f4668e1831850ebcc2fd0b1cd11721947b6dc7c00bf1c6bd3c929ae14f2c7"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0be65ccf618c1e7ac9b849c315cc2e8a8751d9cfdaa43027d4f6624bd587ab7e"}, {file = "charset_normalizer-3.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:53d0a3fa5f8af98a1e261de6a3943ca631c526635eb5817a87a59d9a57ebf48f"}, {file = "charset_normalizer-3.1.0-cp39-cp39-win32.whl", hash = "sha256:a04f86f41a8916fe45ac5024ec477f41f886b3c435da2d4e3d2709b22ab02af1"}, {file = "charset_normalizer-3.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:830d2948a5ec37c386d3170c483063798d7879037492540f10a475e3fd6f244b"}, {file = "charset_normalizer-3.1.0-py3-none-any.whl", hash = "sha256:3d9098b479e78c85080c98e1e35ff40b4a31d8953102bb0fd7d1b6f8a2111a3d"}, ] [[package]] name = "colorama" version = "0.4.6" description = "Cross-platform colored terminal text." category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] [[package]] name = "coverage" version = "5.5" description = "Code coverage measurement for Python" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" files = [ {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] [package.extras] toml = ["toml"] [[package]] name = "cryptography" version = "40.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_universal2.whl", hash = "sha256:8f79b5ff5ad9d3218afb1e7e20ea74da5f76943ee5edb7f76e56ec5161ec782b"}, {file = "cryptography-40.0.2-cp36-abi3-macosx_10_12_x86_64.whl", hash = "sha256:05dc219433b14046c476f6f09d7636b92a1c3e5808b9a6536adf4932b3b2c440"}, {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4df2af28d7bedc84fe45bd49bc35d710aede676e2a4cb7fc6d103a2adc8afe4d"}, {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dcca15d3a19a66e63662dc8d30f8036b07be851a8680eda92d079868f106288"}, {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:a04386fb7bc85fab9cd51b6308633a3c271e3d0d3eae917eebab2fac6219b6d2"}, {file = "cryptography-40.0.2-cp36-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:adc0d980fd2760c9e5de537c28935cc32b9353baaf28e0814df417619c6c8c3b"}, {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:d5a1bd0e9e2031465761dfa920c16b0065ad77321d8a8c1f5ee331021fda65e9"}, {file = "cryptography-40.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a95f4802d49faa6a674242e25bfeea6fc2acd915b5e5e29ac90a32b1139cae1c"}, {file = "cryptography-40.0.2-cp36-abi3-win32.whl", hash = "sha256:aecbb1592b0188e030cb01f82d12556cf72e218280f621deed7d806afd2113f9"}, {file = "cryptography-40.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:b12794f01d4cacfbd3177b9042198f3af1c856eedd0a98f10f141385c809a14b"}, {file = "cryptography-40.0.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:142bae539ef28a1c76794cca7f49729e7c54423f615cfd9b0b1fa90ebe53244b"}, {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:956ba8701b4ffe91ba59665ed170a2ebbdc6fc0e40de5f6059195d9f2b33ca0e"}, {file = "cryptography-40.0.2-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4f01c9863da784558165f5d4d916093737a75203a5c5286fde60e503e4276c7a"}, {file = "cryptography-40.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3daf9b114213f8ba460b829a02896789751626a2a4e7a43a28ee77c04b5e4958"}, {file = "cryptography-40.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:48f388d0d153350f378c7f7b41497a54ff1513c816bcbbcafe5b829e59b9ce5b"}, {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c0764e72b36a3dc065c155e5b22f93df465da9c39af65516fe04ed3c68c92636"}, {file = "cryptography-40.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:cbaba590180cba88cb99a5f76f90808a624f18b169b90a4abb40c1fd8c19420e"}, {file = "cryptography-40.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7a38250f433cd41df7fcb763caa3ee9362777fdb4dc642b9a349721d2bf47404"}, {file = "cryptography-40.0.2.tar.gz", hash = "sha256:c33c0d32b8594fa647d2e01dbccc303478e16fdd7cf98652d5b3ed11aa5e5c99"}, ] [package.dependencies] cffi = ">=1.12" [package.extras] docs = ["sphinx (>=5.3.0)", "sphinx-rtd-theme (>=1.1.1)"] docstest = ["pyenchant (>=1.6.11)", "sphinxcontrib-spelling (>=4.0.1)", "twine (>=1.12.0)"] pep8test = ["black", "check-manifest", "mypy", "ruff"] sdist = ["setuptools-rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] test = ["iso8601", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-shard (>=0.1.2)", "pytest-subtests", "pytest-xdist"] test-randomorder = ["pytest-randomly"] tox = ["tox"] [[package]] name = "distlib" version = "0.3.6" description = "Distribution utilities" category = "dev" optional = false python-versions = "*" files = [ {file = "distlib-0.3.6-py2.py3-none-any.whl", hash = "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e"}, {file = "distlib-0.3.6.tar.gz", hash = "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46"}, ] [[package]] name = "docutils" version = "0.19" description = "Docutils -- Python Documentation Utilities" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "docutils-0.19-py3-none-any.whl", hash = "sha256:5e1de4d849fee02c63b040a4a3fd567f4ab104defd8a5511fbbc24a8a017efbc"}, {file = "docutils-0.19.tar.gz", hash = "sha256:33995a6753c30b7f577febfc2c50411fec6aac7f7ffeb7c4cfe5991072dcf9e6"}, ] [[package]] name = "filelock" version = "3.12.0" description = "A platform independent file lock." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "filelock-3.12.0-py3-none-any.whl", hash = "sha256:ad98852315c2ab702aeb628412cbf7e95b7ce8c3bf9565670b4eaecf1db370a9"}, {file = "filelock-3.12.0.tar.gz", hash = "sha256:fc03ae43288c013d2ea83c8597001b1129db351aad9c57fe2409327916b8e718"}, ] [package.extras] docs = ["furo (>=2023.3.27)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] testing = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "diff-cover (>=7.5)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)", "pytest-timeout (>=2.1)"] [[package]] name = "identify" version = "2.5.22" description = "File identification library for Python" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "identify-2.5.22-py2.py3-none-any.whl", hash = "sha256:f0faad595a4687053669c112004178149f6c326db71ee999ae4636685753ad2f"}, {file = "identify-2.5.22.tar.gz", hash = "sha256:f7a93d6cf98e29bd07663c60728e7a4057615068d7a639d132dc883b2d54d31e"}, ] [package.extras] license = ["ukkonen"] [[package]] name = "idna" version = "3.4" description = "Internationalized Domain Names in Applications (IDNA)" category = "dev" optional = false python-versions = ">=3.5" files = [ {file = "idna-3.4-py3-none-any.whl", hash = "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"}, {file = "idna-3.4.tar.gz", hash = "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4"}, ] [[package]] name = "importlib-metadata" version = "6.5.0" description = "Read metadata from Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "importlib_metadata-6.5.0-py3-none-any.whl", hash = "sha256:03ba783c3a2c69d751b109fc0c94a62c51f581b3d6acf8ed1331b6d5729321ff"}, {file = "importlib_metadata-6.5.0.tar.gz", hash = "sha256:7a8bdf1bc3a726297f5cfbc999e6e7ff6b4fa41b26bba4afc580448624460045"}, ] [package.dependencies] typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] perf = ["ipython"] testing = ["flake8 (<5)", "flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)"] [[package]] name = "importlib-resources" version = "5.12.0" description = "Read resources from Python packages" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "importlib_resources-5.12.0-py3-none-any.whl", hash = "sha256:7b1deeebbf351c7578e09bf2f63fa2ce8b5ffec296e0d349139d43cca061a81a"}, {file = "importlib_resources-5.12.0.tar.gz", hash = "sha256:4be82589bf5c1d7999aedf2a45159d10cb3ca4f19b2271f8792bc8e6da7b22f6"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "iniconfig" version = "2.0.0" description = "brain-dead simple config-ini parsing" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, ] [[package]] name = "jaraco-classes" version = "3.2.3" description = "Utility functions for Python class constructs" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "jaraco.classes-3.2.3-py3-none-any.whl", hash = "sha256:2353de3288bc6b82120752201c6b1c1a14b058267fa424ed5ce5984e3b922158"}, {file = "jaraco.classes-3.2.3.tar.gz", hash = "sha256:89559fa5c1d3c34eff6f631ad80bb21f378dbcbb35dd161fd2c6b93f5be2f98a"}, ] [package.dependencies] more-itertools = "*" [package.extras] docs = ["jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "jeepney" version = "0.8.0" description = "Low-level, pure Python DBus protocol wrapper." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, ] [package.extras] test = ["async-timeout", "pytest", "pytest-asyncio (>=0.17)", "pytest-trio", "testpath", "trio"] trio = ["async_generator", "trio"] [[package]] name = "keyring" version = "23.13.1" description = "Store and access your passwords safely." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "keyring-23.13.1-py3-none-any.whl", hash = "sha256:771ed2a91909389ed6148631de678f82ddc73737d85a927f382a8a1b157898cd"}, {file = "keyring-23.13.1.tar.gz", hash = "sha256:ba2e15a9b35e21908d0aaf4e0a47acc52d6ae33444df0da2b49d41a46ef6d678"}, ] [package.dependencies] importlib-metadata = {version = ">=4.11.4", markers = "python_version < \"3.12\""} importlib-resources = {version = "*", markers = "python_version < \"3.9\""} "jaraco.classes" = "*" jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} pywin32-ctypes = {version = ">=0.2.0", markers = "sys_platform == \"win32\""} SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} [package.extras] completion = ["shtab"] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)"] testing = ["flake8 (<5)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [[package]] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." category = "main" optional = false python-versions = ">=3.7" files = [ {file = "lazy-object-proxy-1.9.0.tar.gz", hash = "sha256:659fb5809fa4629b8a1ac5106f669cfc7bef26fbb389dda53b3e010d1ac4ebae"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b40387277b0ed2d0602b8293b94d7257e17d1479e257b4de114ea11a8cb7f2d7"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8c6cfb338b133fbdbc5cfaa10fe3c6aeea827db80c978dbd13bc9dd8526b7d4"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:721532711daa7db0d8b779b0bb0318fa87af1c10d7fe5e52ef30f8eff254d0cd"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:66a3de4a3ec06cd8af3f61b8e1ec67614fbb7c995d02fa224813cb7afefee701"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:1aa3de4088c89a1b69f8ec0dcc169aa725b0ff017899ac568fe44ddc1396df46"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-win32.whl", hash = "sha256:f0705c376533ed2a9e5e97aacdbfe04cecd71e0aa84c7c0595d02ef93b6e4455"}, {file = "lazy_object_proxy-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:ea806fd4c37bf7e7ad82537b0757999264d5f70c45468447bb2b91afdbe73a6e"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:946d27deaff6cf8452ed0dba83ba38839a87f4f7a9732e8f9fd4107b21e6ff07"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79a31b086e7e68b24b99b23d57723ef7e2c6d81ed21007b6281ebcd1688acb0a"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f699ac1c768270c9e384e4cbd268d6e67aebcfae6cd623b4d7c3bfde5a35db59"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:bfb38f9ffb53b942f2b5954e0f610f1e721ccebe9cce9025a38c8ccf4a5183a4"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:189bbd5d41ae7a498397287c408617fe5c48633e7755287b21d741f7db2706a9"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-win32.whl", hash = "sha256:81fc4d08b062b535d95c9ea70dbe8a335c45c04029878e62d744bdced5141586"}, {file = "lazy_object_proxy-1.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:f2457189d8257dd41ae9b434ba33298aec198e30adf2dcdaaa3a28b9994f6adb"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9e25ef10a39e8afe59a5c348a4dbf29b4868ab76269f81ce1674494e2565a6e"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cbf9b082426036e19c6924a9ce90c740a9861e2bdc27a4834fd0a910742ac1e8"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f5fa4a61ce2438267163891961cfd5e32ec97a2c444e5b842d574251ade27d2"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8fa02eaab317b1e9e03f69aab1f91e120e7899b392c4fc19807a8278a07a97e8"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e7c21c95cae3c05c14aafffe2865bbd5e377cfc1348c4f7751d9dc9a48ca4bda"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win32.whl", hash = "sha256:f12ad7126ae0c98d601a7ee504c1122bcef553d1d5e0c3bfa77b16b3968d2734"}, {file = "lazy_object_proxy-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:edd20c5a55acb67c7ed471fa2b5fb66cb17f61430b7a6b9c3b4a1e40293b1671"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2d0daa332786cf3bb49e10dc6a17a52f6a8f9601b4cf5c295a4f85854d61de63"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cd077f3d04a58e83d04b20e334f678c2b0ff9879b9375ed107d5d07ff160171"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:660c94ea760b3ce47d1855a30984c78327500493d396eac4dfd8bd82041b22be"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:212774e4dfa851e74d393a2370871e174d7ff0ebc980907723bb67d25c8a7c30"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:f0117049dd1d5635bbff65444496c90e0baa48ea405125c088e93d9cf4525b11"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-win32.whl", hash = "sha256:0a891e4e41b54fd5b8313b96399f8b0e173bbbfc03c7631f01efbe29bb0bcf82"}, {file = "lazy_object_proxy-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:9990d8e71b9f6488e91ad25f322898c136b008d87bf852ff65391b004da5e17b"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9e7551208b2aded9c1447453ee366f1c4070602b3d932ace044715d89666899b"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f83ac4d83ef0ab017683d715ed356e30dd48a93746309c8f3517e1287523ef4"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7322c3d6f1766d4ef1e51a465f47955f1e8123caee67dd641e67d539a534d006"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:18b78ec83edbbeb69efdc0e9c1cb41a3b1b1ed11ddd8ded602464c3fc6020494"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:09763491ce220c0299688940f8dc2c5d05fd1f45af1e42e636b2e8b2303e4382"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-win32.whl", hash = "sha256:9090d8e53235aa280fc9239a86ae3ea8ac58eff66a705fa6aa2ec4968b95c821"}, {file = "lazy_object_proxy-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:db1c1722726f47e10e0b5fdbf15ac3b8adb58c091d12b3ab713965795036985f"}, ] [[package]] name = "more-itertools" version = "9.1.0" description = "More routines for operating on iterables, beyond itertools" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, ] [[package]] name = "nodeenv" version = "1.7.0" description = "Node.js virtual environment builder" category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" files = [ {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, ] [package.dependencies] setuptools = "*" [[package]] name = "packaging" version = "23.1" description = "Core utilities for Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] [[package]] name = "pkginfo" version = "1.9.6" description = "Query metadata from sdists / bdists / installed packages." category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "pkginfo-1.9.6-py3-none-any.whl", hash = "sha256:4b7a555a6d5a22169fcc9cf7bfd78d296b0361adad412a346c1226849af5e546"}, {file = "pkginfo-1.9.6.tar.gz", hash = "sha256:8fd5896e8718a4372f0ea9cc9d96f6417c9b986e23a4d116dda26b62cc29d046"}, ] [package.extras] testing = ["pytest", "pytest-cov"] [[package]] name = "platformdirs" version = "3.2.0" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "platformdirs-3.2.0-py3-none-any.whl", hash = "sha256:ebe11c0d7a805086e99506aa331612429a72ca7cd52a1f0d277dc4adc20cb10e"}, {file = "platformdirs-3.2.0.tar.gz", hash = "sha256:d5b638ca397f25f979350ff789db335903d7ea010ab28903f57b27e1b16c2b08"}, ] [package.dependencies] typing-extensions = {version = ">=4.5", markers = "python_version < \"3.8\""} [package.extras] docs = ["furo (>=2022.12.7)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-autodoc-typehints (>=1.22,!=1.23.4)"] test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.2.2)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] [[package]] name = "pluggy" version = "1.0.0" description = "plugin and hook calling mechanisms for python" category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] [package.dependencies] importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} [package.extras] dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] [[package]] name = "pre-commit" version = "2.21.0" description = "A framework for managing and maintaining multi-language pre-commit hooks." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "pre_commit-2.21.0-py2.py3-none-any.whl", hash = "sha256:e2f91727039fc39a92f58a588a25b87f936de6567eed4f0e673e0507edc75bad"}, {file = "pre_commit-2.21.0.tar.gz", hash = "sha256:31ef31af7e474a8d8995027fefdfcf509b5c913ff31f2015b4ec4beb26a6f658"}, ] [package.dependencies] cfgv = ">=2.0.0" identify = ">=1.0.0" importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} nodeenv = ">=0.11.1" pyyaml = ">=5.1" virtualenv = ">=20.10.0" [[package]] name = "py" version = "1.11.0" description = "library with cross-python path, ini-parsing, io, code, log facilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, ] [[package]] name = "py-cpuinfo" version = "9.0.0" description = "Get CPU info with pure Python" category = "dev" optional = false python-versions = "*" files = [ {file = "py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690"}, {file = "py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5"}, ] [[package]] name = "pycparser" version = "2.21" description = "C parser in Python" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] [[package]] name = "pygments" version = "2.15.1" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "Pygments-2.15.1-py3-none-any.whl", hash = "sha256:db2db3deb4b4179f399a09054b023b6a586b76499d36965813c71aa8ed7b5fd1"}, {file = "Pygments-2.15.1.tar.gz", hash = "sha256:8ace4d3c1dd481894b2005f560ead0f9f19ee64fe983366be1a21e171d12775c"}, ] [package.extras] plugins = ["importlib-metadata"] [[package]] name = "pytest" version = "6.2.5" description = "pytest: simple powerful testing with Python" category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, ] [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" packaging = "*" pluggy = ">=0.12,<2.0" py = ">=1.8.2" toml = "*" [package.extras] testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] [[package]] name = "pytest-benchmark" version = "3.4.1" description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "pytest-benchmark-3.4.1.tar.gz", hash = "sha256:40e263f912de5a81d891619032983557d62a3d85843f9a9f30b98baea0cd7b47"}, {file = "pytest_benchmark-3.4.1-py2.py3-none-any.whl", hash = "sha256:36d2b08c4882f6f997fd3126a3d6dfd70f3249cde178ed8bbc0b73db7c20f809"}, ] [package.dependencies] py-cpuinfo = "*" pytest = ">=3.8" [package.extras] aspect = ["aspectlib"] elasticsearch = ["elasticsearch"] histogram = ["pygal", "pygaljs"] [[package]] name = "pytest-cov" version = "2.12.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" files = [ {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, ] [package.dependencies] coverage = ">=5.2.1" pytest = ">=4.6" toml = "*" [package.extras] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] [[package]] name = "pywin32-ctypes" version = "0.2.0" description = "" category = "dev" optional = false python-versions = "*" files = [ {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, ] [[package]] name = "pyyaml" version = "6.0" description = "YAML parser and emitter for Python" category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, {file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"}, {file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"}, {file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"}, {file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"}, {file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"}, {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, ] [[package]] name = "readme-renderer" version = "37.3" description = "readme_renderer is a library for rendering \"readme\" descriptions for Warehouse" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "readme_renderer-37.3-py3-none-any.whl", hash = "sha256:f67a16caedfa71eef48a31b39708637a6f4664c4394801a7b0d6432d13907343"}, {file = "readme_renderer-37.3.tar.gz", hash = "sha256:cd653186dfc73055656f090f227f5cb22a046d7f71a841dfa305f55c9a513273"}, ] [package.dependencies] bleach = ">=2.1.0" docutils = ">=0.13.1" Pygments = ">=2.5.1" [package.extras] md = ["cmarkgfm (>=0.8.0)"] [[package]] name = "requests" version = "2.28.2" description = "Python HTTP for Humans." category = "dev" optional = false python-versions = ">=3.7, <4" files = [ {file = "requests-2.28.2-py3-none-any.whl", hash = "sha256:64299f4909223da747622c030b781c0d7811e359c37124b4bd368fb8c6518baa"}, {file = "requests-2.28.2.tar.gz", hash = "sha256:98b1b2782e3c6c4904938b84c0eb932721069dfdb9134313beff7c83c2df24bf"}, ] [package.dependencies] certifi = ">=2017.4.17" charset-normalizer = ">=2,<4" idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "requests-toolbelt" version = "0.10.1" description = "A utility belt for advanced users of python-requests" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" files = [ {file = "requests-toolbelt-0.10.1.tar.gz", hash = "sha256:62e09f7ff5ccbda92772a29f394a49c3ad6cb181d568b1337626b2abb628a63d"}, {file = "requests_toolbelt-0.10.1-py2.py3-none-any.whl", hash = "sha256:18565aa58116d9951ac39baa288d3adb5b3ff975c4f25eee78555d89e8f247f7"}, ] [package.dependencies] requests = ">=2.0.1,<3.0.0" [[package]] name = "rfc3986" version = "2.0.0" description = "Validating URI References per RFC 3986" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd"}, {file = "rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c"}, ] [package.extras] idna2008 = ["idna"] [[package]] name = "secretstorage" version = "3.3.3" description = "Python bindings to FreeDesktop.org Secret Service API" category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99"}, {file = "SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77"}, ] [package.dependencies] cryptography = ">=2.0" jeepney = ">=0.6" [[package]] name = "semver" version = "3.0.0" description = "Python helper for Semantic Versioning (https://semver.org)" category = "main" optional = false python-versions = ">=3.7" files = [ {file = "semver-3.0.0-py3-none-any.whl", hash = "sha256:ab4f69fb1d1ecfb5d81f96411403d7a611fa788c45d252cf5b408025df3ab6ce"}, {file = "semver-3.0.0.tar.gz", hash = "sha256:94df43924c4521ec7d307fc86da1531db6c2c33d9d5cdc3e64cca0eb68569269"}, ] [[package]] name = "setuptools" version = "67.7.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" category = "main" optional = false python-versions = ">=3.7" files = [ {file = "setuptools-67.7.0-py3-none-any.whl", hash = "sha256:888be97fde8cc3afd60f7784e678fa29ee13c4e5362daa7104a93bba33646c50"}, {file = "setuptools-67.7.0.tar.gz", hash = "sha256:b7e53a01c6c654d26d2999ee033d8c6125e5fa55f03b7b193f937ae7ac999f22"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [[package]] name = "six" version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] [[package]] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" files = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] [[package]] name = "tomli" version = "2.0.1" description = "A lil' TOML parser" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] [[package]] name = "tox" version = "3.28.0" description = "tox is a generic virtualenv management and test command line tool" category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ {file = "tox-3.28.0-py2.py3-none-any.whl", hash = "sha256:57b5ab7e8bb3074edc3c0c0b4b192a4f3799d3723b2c5b76f1fa9f2d40316eea"}, {file = "tox-3.28.0.tar.gz", hash = "sha256:d0d28f3fe6d6d7195c27f8b054c3e99d5451952b54abdae673b71609a581f640"}, ] [package.dependencies] colorama = {version = ">=0.4.1", markers = "platform_system == \"Windows\""} filelock = ">=3.0.0" importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} packaging = ">=14" pluggy = ">=0.12.0" py = ">=1.4.17" six = ">=1.14.0" tomli = {version = ">=2.0.1", markers = "python_version >= \"3.7\" and python_version < \"3.11\""} virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7" [package.extras] docs = ["pygments-github-lexers (>=0.0.5)", "sphinx (>=2.0.0)", "sphinxcontrib-autoprogram (>=0.1.5)", "towncrier (>=18.5.0)"] testing = ["flaky (>=3.4.0)", "freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "psutil (>=5.6.1)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-randomly (>=1.0.0)"] [[package]] name = "tqdm" version = "4.65.0" description = "Fast, Extensible Progress Meter" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "tqdm-4.65.0-py3-none-any.whl", hash = "sha256:c4f53a17fe37e132815abceec022631be8ffe1b9381c2e6e30aa70edc99e9671"}, {file = "tqdm-4.65.0.tar.gz", hash = "sha256:1871fb68a86b8fb3b59ca4cdd3dcccbc7e6d613eeed31f4c332531977b89beb5"}, ] [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} [package.extras] dev = ["py-make (>=0.1.0)", "twine", "wheel"] notebook = ["ipywidgets (>=6)"] slack = ["slack-sdk"] telegram = ["requests"] [[package]] name = "twine" version = "3.8.0" description = "Collection of utilities for publishing packages on PyPI" category = "dev" optional = false python-versions = ">=3.6" files = [ {file = "twine-3.8.0-py3-none-any.whl", hash = "sha256:d0550fca9dc19f3d5e8eadfce0c227294df0a2a951251a4385797c8a6198b7c8"}, {file = "twine-3.8.0.tar.gz", hash = "sha256:8efa52658e0ae770686a13b675569328f1fba9837e5de1867bfe5f46a9aefe19"}, ] [package.dependencies] colorama = ">=0.4.3" importlib-metadata = ">=3.6" keyring = ">=15.1" pkginfo = ">=1.8.1" readme-renderer = ">=21.0" requests = ">=2.20" requests-toolbelt = ">=0.8.0,<0.9.0 || >0.9.0" rfc3986 = ">=1.4.0" tqdm = ">=4.14" urllib3 = ">=1.26.0" [[package]] name = "typed-ast" version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "main" optional = false python-versions = ">=3.6" files = [ {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] [[package]] name = "types-toml" version = "0.10.8.6" description = "Typing stubs for toml" category = "dev" optional = false python-versions = "*" files = [ {file = "types-toml-0.10.8.6.tar.gz", hash = "sha256:6d3ac79e36c9ee593c5d4fb33a50cca0e3adceb6ef5cff8b8e5aef67b4c4aaf2"}, {file = "types_toml-0.10.8.6-py3-none-any.whl", hash = "sha256:de7b2bb1831d6f7a4b554671ffe5875e729753496961b3e9b202745e4955dafa"}, ] [[package]] name = "typing-extensions" version = "4.5.0" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false python-versions = ">=3.7" files = [ {file = "typing_extensions-4.5.0-py3-none-any.whl", hash = "sha256:fb33085c39dd998ac16d1431ebc293a8b3eedd00fd4a32de0ff79002c19511b4"}, {file = "typing_extensions-4.5.0.tar.gz", hash = "sha256:5cb5f4a79139d699607b3ef622a1dedafa84e115ab0024e0d9c044a9479ca7cb"}, ] [[package]] name = "urllib3" version = "1.26.15" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" files = [ {file = "urllib3-1.26.15-py2.py3-none-any.whl", hash = "sha256:aa751d169e23c7479ce47a0cb0da579e3ede798f994f5816a74e4f4500dcea42"}, {file = "urllib3-1.26.15.tar.gz", hash = "sha256:8a388717b9476f934a21484e8c8e61875ab60644d29b9b39e11e4b9dc1c6b305"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"] secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "virtualenv" version = "20.22.0" description = "Virtual Python Environment builder" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "virtualenv-20.22.0-py3-none-any.whl", hash = "sha256:48fd3b907b5149c5aab7c23d9790bea4cac6bc6b150af8635febc4cfeab1275a"}, {file = "virtualenv-20.22.0.tar.gz", hash = "sha256:278753c47aaef1a0f14e6db8a4c5e1e040e90aea654d0fc1dc7e0d8a42616cc3"}, ] [package.dependencies] distlib = ">=0.3.6,<1" filelock = ">=3.11,<4" importlib-metadata = {version = ">=6.4.1", markers = "python_version < \"3.8\""} platformdirs = ">=3.2,<4" [package.extras] docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.1.3)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=22.12)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.3)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.3.1)", "pytest-env (>=0.8.1)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.10)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)"] [[package]] name = "webencodings" version = "0.5.1" description = "Character encoding aliases for legacy web content" category = "dev" optional = false python-versions = "*" files = [ {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, ] [[package]] name = "wrapt" version = "1.15.0" description = "Module for decorators, wrappers and monkey patching." category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" files = [ {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, ] [[package]] name = "zipp" version = "3.15.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "dev" optional = false python-versions = ">=3.7" files = [ {file = "zipp-3.15.0-py3-none-any.whl", hash = "sha256:48904fc76a60e542af151aded95726c1a5c34ed43ab4134b597665c86d7ad556"}, {file = "zipp-3.15.0.tar.gz", hash = "sha256:112929ad649da941c23de50f356a2b5570c954b65150642bccdd66bf194d224b"}, ] [package.extras] docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "2.0" python-versions = ">=3.7,<4.0" content-hash = "1ca9b3ca847beadbf05c833083a5e9193fe59a927d1d5ab94e0dcc02212c4be7" requirements-detector-1.2.2/pyproject.toml000066400000000000000000000025471443205141000207640ustar00rootroot00000000000000[tool.poetry] name = "requirements-detector" version = "1.2.2" authors = ["Landscape.io "] classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'Operating System :: Unix', 'Topic :: Software Development :: Quality Assurance', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', 'License :: OSI Approved :: MIT License', ] license = "MIT" keywords = ["python","requirements detector"] description = "Python tool to find and list requirements of a Python project" readme = "README.md" homepage = "https://github.com/landscapeio/requirements-detector" packages = [ { include = "requirements_detector/"} ] [tool.poetry.scripts] detect-requirements = 'requirements_detector.run:run' [tool.poetry.dependencies] python = ">=3.7,<4.0" astroid = "^2.0" packaging = ">=21.3" toml = "^0.10.2" semver = "^3.0.0" [tool.poetry.dev-dependencies] tox = "^3.24.5" pre-commit = "^2.17.0" pytest = "^6.2.4" coverage = "^5.5" twine = "^3.8.0" pytest-benchmark = "^3.4.1" pytest-cov = "^2.12.1" types-toml = "^0.10.4" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" requirements-detector-1.2.2/requirements_detector/000077500000000000000000000000001443205141000224545ustar00rootroot00000000000000requirements-detector-1.2.2/requirements_detector/__init__.py000066400000000000000000000004151443205141000245650ustar00rootroot00000000000000from requirements_detector.detect import ( # from_setup_py, CouldNotParseRequirements, RequirementsNotFound, find_requirements, from_pyproject_toml, from_requirements_blob, from_requirements_dir, from_requirements_txt, from_setup_py, ) requirements-detector-1.2.2/requirements_detector/__main__.py000066400000000000000000000000731443205141000245460ustar00rootroot00000000000000from .run import run if __name__ == "__main__": run() requirements-detector-1.2.2/requirements_detector/detect.py000066400000000000000000000133701443205141000243020ustar00rootroot00000000000000import re from pathlib import Path from typing import List, Union import toml from .exceptions import CouldNotParseRequirements, RequirementsNotFound from .handle_setup import from_setup_py from .poetry_semver import parse_constraint from .requirement import DetectedRequirement __all__ = [ "find_requirements", "from_requirements_txt", "from_requirements_dir", "from_requirements_blob", "from_pyproject_toml", "from_setup_py", "RequirementsNotFound", "CouldNotParseRequirements", ] _PIP_OPTIONS = ( "-i", "--index-url", "--extra-index-url", "--no-index", "-f", "--find-links", "-r", ) P = Union[str, Path] def find_requirements(path: P) -> List[DetectedRequirement]: """ This method tries to determine the requirements of a particular project by inspecting the possible places that they could be defined. It will attempt, in order: 1) to parse setup.py in the root for an install_requires value 2) to read a requirements.txt file or a requirements.pip in the root 3) to read all .txt files in a folder called 'requirements' in the root 4) to read files matching "*requirements*.txt" and "*reqs*.txt" in the root, excluding any starting or ending with 'test' If one of these succeeds, then a list of pkg_resources.Requirement's will be returned. If none can be found, then a RequirementsNotFound will be raised """ requirements = [] if isinstance(path, str): path = Path(path) setup_py = path / "setup.py" if setup_py.exists() and setup_py.is_file(): try: requirements = from_setup_py(setup_py) requirements.sort() return requirements except CouldNotParseRequirements: pass poetry_toml = path / "pyproject.toml" if poetry_toml.exists() and poetry_toml.is_file(): try: requirements = from_pyproject_toml(poetry_toml) if len(requirements) > 0: requirements.sort() return requirements except CouldNotParseRequirements: pass for reqfile_name in ("requirements.txt", "requirements.pip"): reqfile = path / reqfile_name if reqfile.exists and reqfile.is_file(): try: requirements += from_requirements_txt(reqfile) except CouldNotParseRequirements as e: pass requirements_dir = path / "requirements" if requirements_dir.exists() and requirements_dir.is_dir(): from_dir = from_requirements_dir(requirements_dir) if from_dir is not None: requirements += from_dir from_blob = from_requirements_blob(path) if from_blob is not None: requirements += from_blob requirements = list(set(requirements)) if len(requirements) > 0: requirements.sort() return requirements raise RequirementsNotFound def from_pyproject_toml(toml_file: P) -> List[DetectedRequirement]: requirements = [] if isinstance(toml_file, str): toml_file = Path(toml_file) parsed = toml.load(toml_file) poetry_section = parsed.get("tool", {}).get("poetry", {}) dependencies = poetry_section.get("dependencies", {}) dependencies.update(poetry_section.get("dev-dependencies", {})) for name, spec in dependencies.items(): if name.lower() == "python": continue if isinstance(spec, dict): if "version" in spec: spec = spec["version"] else: req = DetectedRequirement.parse(f"{name}", toml_file) if req is not None: requirements.append(req) continue parsed_spec = str(parse_constraint(spec)) if "," not in parsed_spec and "<" not in parsed_spec and ">" not in parsed_spec and "=" not in parsed_spec: parsed_spec = f"=={parsed_spec}" req = DetectedRequirement.parse(f"{name}{parsed_spec}", toml_file) if req is not None: requirements.append(req) return requirements def from_requirements_txt(requirements_file: P) -> List[DetectedRequirement]: # see http://www.pip-installer.org/en/latest/logic.html requirements = [] if isinstance(requirements_file, str): requirements_file = Path(requirements_file) with requirements_file.open() as f: for req in f.readlines(): if req.strip() == "": # empty line continue if req.strip().startswith("#"): # this is a comment continue if req.strip().split()[0] in _PIP_OPTIONS: # this is a pip option continue detected = DetectedRequirement.parse(req, requirements_file) if detected is None: continue requirements.append(detected) return requirements def from_requirements_dir(path: P) -> List[DetectedRequirement]: requirements = [] if isinstance(path, str): path = Path(path) for entry in path.iterdir(): if not entry.is_file(): continue if entry.name.endswith(".txt") or entry.name.endswith(".pip"): requirements += from_requirements_txt(entry) return list(set(requirements)) def from_requirements_blob(path: P) -> List[DetectedRequirement]: requirements = [] if isinstance(path, str): path = Path(path) for entry in path.iterdir(): if not entry.is_file(): continue m = re.match(r"^(\w*)req(uirement)?s(\w*)\.txt$", entry.name) if m is None: continue if m.group(1).startswith("test") or m.group(3).endswith("test"): continue requirements += from_requirements_txt(entry) return requirements requirements-detector-1.2.2/requirements_detector/exceptions.py000066400000000000000000000001471443205141000252110ustar00rootroot00000000000000class RequirementsNotFound(Exception): pass class CouldNotParseRequirements(Exception): pass requirements-detector-1.2.2/requirements_detector/formatters.py000066400000000000000000000005241443205141000252150ustar00rootroot00000000000000import sys from typing import List from .requirement import DetectedRequirement def requirements_file(requirements_list: List[DetectedRequirement]) -> None: for requirement in requirements_list: sys.stdout.write(requirement.pip_format()) sys.stdout.write("\n") FORMATTERS = {"requirements_file": requirements_file} requirements-detector-1.2.2/requirements_detector/handle_setup.py000066400000000000000000000076371443205141000255160ustar00rootroot00000000000000from pathlib import Path from typing import Union from astroid import MANAGER, AstroidBuildingException, AstroidSyntaxError from astroid.builder import AstroidBuilder from astroid.nodes import Assign, AssignName, Call, Const, Keyword, List, Name, Tuple from .exceptions import CouldNotParseRequirements from .requirement import DetectedRequirement class SetupWalker: def __init__(self, ast): self._ast = ast self._setup_call = None self._top_level_assigns = {} self.walk() def walk(self, node=None): top = node is None node = node or self._ast # test to see if this is a call to setup() if isinstance(node, Call): for child_node in node.get_children(): if isinstance(child_node, Name) and child_node.name == "setup": # TODO: what if this isn't actually the distutils setup? self._setup_call = node for child_node in node.get_children(): if top and isinstance(child_node, Assign): for target in child_node.targets: if isinstance(target, AssignName): self._top_level_assigns[target.name] = child_node.value self.walk(child_node) def _get_list_value(self, list_node): values = [] for child_node in list_node.get_children(): if not isinstance(child_node, Const): # we can't handle anything fancy, only constant values raise CouldNotParseRequirements values.append(child_node.value) return values def get_requires(self): # first, if we have a call to setup, then we can see what its "install_requires" argument is if not self._setup_call: raise CouldNotParseRequirements found_requirements = [] for child_node in self._setup_call.get_children(): if not isinstance(child_node, Keyword): # do we want to try to handle positional arguments? continue if child_node.arg not in ("install_requires", "requires"): continue if isinstance(child_node.value, (List, Tuple)): # joy! this is a simple list or tuple of requirements # this is a Keyword -> List or Keyword -> Tuple found_requirements += self._get_list_value(child_node.value) continue if isinstance(child_node.value, Name): # otherwise, it's referencing a value defined elsewhere # this will be a Keyword -> Name try: reqs = self._top_level_assigns[child_node.value.name] except KeyError: raise CouldNotParseRequirements else: if isinstance(reqs, (List, Tuple)): found_requirements += self._get_list_value(reqs) continue # otherwise it's something funky and we can't handle it raise CouldNotParseRequirements # if we've fallen off the bottom with nothing in our list of requirements, # we simply didn't find anything useful if len(found_requirements) > 0: return found_requirements raise CouldNotParseRequirements def from_setup_py(setup_file: Union[str, Path]): if isinstance(setup_file, str): setup_file = Path(setup_file) try: ast = AstroidBuilder(MANAGER).string_build(setup_file.open().read()) except (SyntaxError, AstroidBuildingException, AstroidSyntaxError): # if the setup file is broken, we can't do much about that... raise CouldNotParseRequirements walker = SetupWalker(ast) requirements = [] for req in walker.get_requires(): requirements.append(DetectedRequirement.parse(req, setup_file)) return [requirement for requirement in requirements if requirement is not None] requirements-detector-1.2.2/requirements_detector/poetry_semver/000077500000000000000000000000001443205141000253575ustar00rootroot00000000000000requirements-detector-1.2.2/requirements_detector/poetry_semver/README.md000066400000000000000000000004051443205141000266350ustar00rootroot00000000000000poetry-semver ============= This is a carbon-copy of the original, and now archived, poetry-semver package originally located at https://github.com/python-poetry/semver It functionality is still needed, so a drag and drop here was done to ensure portability. requirements-detector-1.2.2/requirements_detector/poetry_semver/__init__.py000066400000000000000000000111111443205141000274630ustar00rootroot00000000000000import re from .empty_constraint import EmptyConstraint from .patterns import ( BASIC_CONSTRAINT, CARET_CONSTRAINT, TILDE_CONSTRAINT, TILDE_PEP440_CONSTRAINT, X_CONSTRAINT, ) from .version import Version from .version_constraint import VersionConstraint from .version_range import VersionRange from .version_union import VersionUnion __version__ = "0.1.0" def parse_constraint(constraints): # type: (str) -> VersionConstraint if constraints == "*": return VersionRange() or_constraints = re.split(r"\s*\|\|?\s*", constraints.strip()) or_groups = [] for constraints in or_constraints: and_constraints = re.split("(?< ,]) *(? 1: for constraint in and_constraints: constraint_objects.append(parse_single_constraint(constraint)) else: constraint_objects.append(parse_single_constraint(and_constraints[0])) if len(constraint_objects) == 1: constraint = constraint_objects[0] else: constraint = constraint_objects[0] for next_constraint in constraint_objects[1:]: constraint = constraint.intersect(next_constraint) or_groups.append(constraint) if len(or_groups) == 1: return or_groups[0] else: return VersionUnion.of(*or_groups) def parse_single_constraint(constraint): # type: (str) -> VersionConstraint m = re.match(r"(?i)^v?[xX*](\.[xX*])*$", constraint) if m: return VersionRange() # Tilde range m = TILDE_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group(1)) high = version.stable.next_minor if len(m.group(1).split(".")) == 1: high = version.stable.next_major return VersionRange(version, high, include_min=True, always_include_max_prerelease=True) # PEP 440 Tilde range (~=) m = TILDE_PEP440_CONSTRAINT.match(constraint) if m: precision = 1 if m.group(3): precision += 1 if m.group(4): precision += 1 version = Version.parse(m.group(1)) if precision == 2: low = version high = version.stable.next_major else: low = Version(version.major, version.minor, version.patch) high = version.stable.next_minor return VersionRange(low, high, include_min=True, always_include_max_prerelease=True) # Caret range m = CARET_CONSTRAINT.match(constraint) if m: version = Version.parse(m.group(1)) return VersionRange( version, version.next_breaking, include_min=True, always_include_max_prerelease=True, ) # X Range m = X_CONSTRAINT.match(constraint) if m: op = m.group(1) major = int(m.group(2)) minor = m.group(3) if minor is not None: version = Version(major, int(minor), 0) result = VersionRange( version, version.next_minor, include_min=True, always_include_max_prerelease=True, ) else: if major == 0: result = VersionRange(max=Version(1, 0, 0)) else: version = Version(major, 0, 0) result = VersionRange( version, version.next_major, include_min=True, always_include_max_prerelease=True, ) if op == "!=": result = VersionRange().difference(result) return result # Basic comparator m = BASIC_CONSTRAINT.match(constraint) if m: op = m.group(1) version = m.group(2) if version == "dev": version = "0.0-dev" try: version = Version.parse(version) except ValueError: raise ValueError("Could not parse version constraint: {}".format(constraint)) if op == "<": return VersionRange(max=version) elif op == "<=": return VersionRange(max=version, include_max=True) elif op == ">": return VersionRange(min=version) elif op == ">=": return VersionRange(min=version, include_min=True) elif op == "!=": return VersionUnion(VersionRange(max=version), VersionRange(min=version)) else: return version raise ValueError("Could not parse version constraint: {}".format(constraint)) requirements-detector-1.2.2/requirements_detector/poetry_semver/empty_constraint.py000066400000000000000000000010621443205141000313320ustar00rootroot00000000000000from .version_constraint import VersionConstraint class EmptyConstraint(VersionConstraint): def is_empty(self): return True def is_any(self): return False def allows(self, version): return False def allows_all(self, other): return other.is_empty() def allows_any(self, other): return False def intersect(self, other): return self def union(self, other): return other def difference(self, other): return self def __str__(self): return "" requirements-detector-1.2.2/requirements_detector/poetry_semver/exceptions.py000066400000000000000000000000561443205141000301130ustar00rootroot00000000000000class ParseVersionError(ValueError): pass requirements-detector-1.2.2/requirements_detector/poetry_semver/patterns.py000066400000000000000000000013451443205141000275740ustar00rootroot00000000000000import re MODIFIERS = ( "[._-]?" r"((?!post)(?:beta|b|c|pre|RC|alpha|a|patch|pl|p|dev)(?:(?:[.-]?\d+)*)?)?" r"([+-]?([0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*))?" ) _COMPLETE_VERSION = r"v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?".format(MODIFIERS) COMPLETE_VERSION = re.compile("(?i)" + _COMPLETE_VERSION) CARET_CONSTRAINT = re.compile(r"(?i)^\^({})$".format(_COMPLETE_VERSION)) TILDE_CONSTRAINT = re.compile("(?i)^~(?!=)({})$".format(_COMPLETE_VERSION)) TILDE_PEP440_CONSTRAINT = re.compile("(?i)^~=({})$".format(_COMPLETE_VERSION)) X_CONSTRAINT = re.compile(r"^(!=|==)?\s*v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.[xX*])+$") BASIC_CONSTRAINT = re.compile(r"(?i)^(<>|!=|>=?|<=?|==?)?\s*({}|dev)".format(_COMPLETE_VERSION)) requirements-detector-1.2.2/requirements_detector/poetry_semver/version.py000066400000000000000000000276401443205141000274270ustar00rootroot00000000000000import re from typing import List, Optional, Union from .empty_constraint import EmptyConstraint from .exceptions import ParseVersionError from .patterns import COMPLETE_VERSION from .version_constraint import VersionConstraint from .version_range import VersionRange from .version_union import VersionUnion class Version(VersionRange): """ A parsed semantic version number. """ def __init__( self, major, # type: int minor=None, # type: Optional[int] patch=None, # type: Optional[int] rest=None, # type: Optional[int] pre=None, # type: Optional[str] build=None, # type: Optional[str] text=None, # type: Optional[str] precision=None, # type: Optional[int] ): # type: (...) -> None self._major = int(major) self._precision = None if precision is None: self._precision = 1 if minor is None: minor = 0 else: if self._precision is not None: self._precision += 1 self._minor = int(minor) if patch is None: patch = 0 else: if self._precision is not None: self._precision += 1 if rest is None: rest = 0 else: if self._precision is not None: self._precision += 1 if precision is not None: self._precision = precision self._patch = int(patch) self._rest = int(rest) if text is None: parts = [str(major)] if self._precision >= 2 or minor != 0: parts.append(str(minor)) if self._precision >= 3 or patch != 0: parts.append(str(patch)) if self._precision >= 4 or rest != 0: parts.append(str(rest)) text = ".".join(parts) if pre: text += "-{}".format(pre) if build: text += "+{}".format(build) self._text = text pre = self._normalize_prerelease(pre) self._prerelease = [] if pre is not None: self._prerelease = self._split_parts(pre) build = self._normalize_build(build) self._build = [] if build is not None: if build.startswith(("-", "+")): build = build[1:] self._build = self._split_parts(build) @property def major(self): # type: () -> int return self._major @property def minor(self): # type: () -> int return self._minor @property def patch(self): # type: () -> int return self._patch @property def rest(self): # type: () -> int return self._rest @property def prerelease(self): # type: () -> List[str] return self._prerelease @property def build(self): # type: () -> List[str] return self._build @property def text(self): return self._text @property def precision(self): # type: () -> int return self._precision @property def stable(self): if not self.is_prerelease(): return self return self.next_patch @property def next_major(self): # type: () -> Version if self.is_prerelease() and self.minor == 0 and self.patch == 0: return Version(self.major, self.minor, self.patch) return self._increment_major() @property def next_minor(self): # type: () -> Version if self.is_prerelease() and self.patch == 0: return Version(self.major, self.minor, self.patch) return self._increment_minor() @property def next_patch(self): # type: () -> Version if self.is_prerelease(): return Version(self.major, self.minor, self.patch) return self._increment_patch() @property def next_breaking(self): # type: () -> Version if self.major == 0: if self.minor != 0: return self._increment_minor() if self._precision == 1: return self._increment_major() elif self._precision == 2: return self._increment_minor() return self._increment_patch() return self._increment_major() @property def first_prerelease(self): # type: () -> Version return Version.parse("{}.{}.{}-alpha.0".format(self.major, self.minor, self.patch)) @property def min(self): return self @property def max(self): return self @property def full_max(self): return self @property def include_min(self): return True @property def include_max(self): return True @classmethod def parse(cls, text): # type: (str) -> Version try: match = COMPLETE_VERSION.match(text) except TypeError: match = None if match is None: raise ParseVersionError('Unable to parse "{}".'.format(text)) text = text.rstrip(".") major = int(match.group(1)) minor = int(match.group(2)) if match.group(2) else None patch = int(match.group(3)) if match.group(3) else None rest = int(match.group(4)) if match.group(4) else None pre = match.group(5) build = match.group(6) if build: build = build.lstrip("+") return Version(major, minor, patch, rest, pre, build, text) def is_any(self): return False def is_empty(self): return False def is_prerelease(self): # type: () -> bool return len(self._prerelease) > 0 def allows(self, version): # type: (Version) -> bool return self == version def allows_all(self, other): # type: (VersionConstraint) -> bool return other.is_empty() or other == self def allows_any(self, other): # type: (VersionConstraint) -> bool return other.allows(self) def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint if other.allows(self): return self return EmptyConstraint() def union(self, other): # type: (VersionConstraint) -> VersionConstraint from .version_range import VersionRange if other.allows(self): return other if isinstance(other, VersionRange): if other.min == self: return VersionRange( other.min, other.max, include_min=True, include_max=other.include_max, ) if other.max == self: return VersionRange( other.min, other.max, include_min=other.include_min, include_max=True, ) return VersionUnion.of(self, other) def difference(self, other): # type: (VersionConstraint) -> VersionConstraint if other.allows(self): return EmptyConstraint() return self def equals_without_prerelease(self, other): # type: (Version) -> bool return self.major == other.major and self.minor == other.minor and self.patch == other.patch def _increment_major(self): # type: () -> Version return Version(self.major + 1, 0, 0, precision=self._precision) def _increment_minor(self): # type: () -> Version return Version(self.major, self.minor + 1, 0, precision=self._precision) def _increment_patch(self): # type: () -> Version return Version(self.major, self.minor, self.patch + 1, precision=self._precision) def _normalize_prerelease(self, pre): # type: (str) -> str if not pre: return m = re.match(r"(?i)^(a|alpha|b|beta|c|pre|rc|dev)[-.]?(\d+)?$", pre) if not m: return modifier = m.group(1) number = m.group(2) if number is None: number = 0 if modifier == "a": modifier = "alpha" elif modifier == "b": modifier = "beta" elif modifier in {"c", "pre"}: modifier = "rc" elif modifier == "dev": modifier = "alpha" return "{}.{}".format(modifier, number) def _normalize_build(self, build): # type: (str) -> str if not build: return if build.startswith("post"): build = build.lstrip("post") if not build: return return build def _split_parts(self, text): # type: (str) -> List[Union[str, int]] parts = text.split(".") for i, part in enumerate(parts): try: parts[i] = int(part) except (TypeError, ValueError): continue return parts def __lt__(self, other): return self._cmp(other) < 0 def __le__(self, other): return self._cmp(other) <= 0 def __gt__(self, other): return self._cmp(other) > 0 def __ge__(self, other): return self._cmp(other) >= 0 def _cmp(self, other): if not isinstance(other, VersionConstraint): return NotImplemented if not isinstance(other, Version): return -other._cmp(self) if self.major != other.major: return self._cmp_parts(self.major, other.major) if self.minor != other.minor: return self._cmp_parts(self.minor, other.minor) if self.patch != other.patch: return self._cmp_parts(self.patch, other.patch) if self.rest != other.rest: return self._cmp_parts(self.rest, other.rest) # Pre-releases always come before no pre-release string. if not self.is_prerelease() and other.is_prerelease(): return 1 if not other.is_prerelease() and self.is_prerelease(): return -1 comparison = self._cmp_lists(self.prerelease, other.prerelease) if comparison != 0: return comparison # Builds always come after no build string. if not self.build and other.build: return -1 if not other.build and self.build: return 1 return self._cmp_lists(self.build, other.build) def _cmp_parts(self, a, b): if a < b: return -1 elif a > b: return 1 return 0 def _cmp_lists(self, a, b): # type: (List, List) -> int for i in range(max(len(a), len(b))): a_part = None if i < len(a): a_part = a[i] b_part = None if i < len(b): b_part = b[i] if a_part == b_part: continue # Missing parts come after present ones. if a_part is None: return -1 if b_part is None: return 1 if isinstance(a_part, int): if isinstance(b_part, int): return self._cmp_parts(a_part, b_part) return -1 else: if isinstance(b_part, int): return 1 return self._cmp_parts(a_part, b_part) return 0 def __eq__(self, other): # type: (Version) -> bool if not isinstance(other, Version): return NotImplemented return ( self._major == other.major and self._minor == other.minor and self._patch == other.patch and self._rest == other.rest and self._prerelease == other.prerelease and self._build == other.build ) def __ne__(self, other): return not self == other def __str__(self): return self._text def __repr__(self): return "".format(str(self)) def __hash__(self): return hash( ( self.major, self.minor, self.patch, ".".join(str(p) for p in self.prerelease), ".".join(str(p) for p in self.build), ) ) requirements-detector-1.2.2/requirements_detector/poetry_semver/version_constraint.py000066400000000000000000000015461443205141000316700ustar00rootroot00000000000000import semver class VersionConstraint: def is_empty(self): # type: () -> bool raise NotImplementedError() def is_any(self): # type: () -> bool raise NotImplementedError() def allows(self, version): # type: (semver.Version) -> bool raise NotImplementedError() def allows_all(self, other): # type: (VersionConstraint) -> bool raise NotImplementedError() def allows_any(self, other): # type: (VersionConstraint) -> bool raise NotImplementedError() def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint raise NotImplementedError() def union(self, other): # type: (VersionConstraint) -> VersionConstraint raise NotImplementedError() def difference(self, other): # type: (VersionConstraint) -> VersionConstraint raise NotImplementedError() requirements-detector-1.2.2/requirements_detector/poetry_semver/version_range.py000066400000000000000000000315731443205141000306030ustar00rootroot00000000000000from typing import List import semver from .empty_constraint import EmptyConstraint from .version_constraint import VersionConstraint from .version_union import VersionUnion class VersionRange(VersionConstraint): def __init__( self, min=None, max=None, include_min=False, include_max=False, always_include_max_prerelease=False, ): full_max = max if ( always_include_max_prerelease and not include_max and not full_max.is_prerelease() and not full_max.build and (min is None or not min.is_prerelease() or not min.equals_without_prerelease(full_max)) ): full_max = full_max.first_prerelease self._min = min self._max = max self._full_max = full_max self._include_min = include_min self._include_max = include_max @property def min(self): return self._min @property def max(self): return self._max @property def full_max(self): return self._full_max @property def include_min(self): return self._include_min @property def include_max(self): return self._include_max def is_empty(self): return False def is_any(self): return self._min is None and self._max is None def allows(self, other): # type: (semver.Version) -> bool if self._min is not None: if other < self._min: return False if not self._include_min and other == self._min: return False if self._max is not None: if other > self._max: return False if not self._include_max and other == self._max: return False return True def allows_all(self, other): # type: (VersionConstraint) -> bool from .version import Version if other.is_empty(): return True if isinstance(other, Version): return self.allows(other) if isinstance(other, VersionUnion): return all([self.allows_all(constraint) for constraint in other.ranges]) if isinstance(other, VersionRange): return not other.allows_lower(self) and not other.allows_higher(self) raise ValueError("Unknown VersionConstraint type {}.".format(other)) def allows_any(self, other): # type: (VersionConstraint) -> bool from .version import Version if other.is_empty(): return False if isinstance(other, Version): return self.allows(other) if isinstance(other, VersionUnion): return any([self.allows_any(constraint) for constraint in other.ranges]) if isinstance(other, VersionRange): return not other.is_strictly_lower(self) and not other.is_strictly_higher(self) raise ValueError("Unknown VersionConstraint type {}.".format(other)) def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint from .version import Version if other.is_empty(): return other if isinstance(other, VersionUnion): return other.intersect(self) # A range and a Version just yields the version if it's in the range. if isinstance(other, Version): if self.allows(other): return other return EmptyConstraint() if not isinstance(other, VersionRange): raise ValueError("Unknown VersionConstraint type {}.".format(other)) if self.allows_lower(other): if self.is_strictly_lower(other): return EmptyConstraint() intersect_min = other.min intersect_include_min = other.include_min else: if other.is_strictly_lower(self): return EmptyConstraint() intersect_min = self._min intersect_include_min = self._include_min if self.allows_higher(other): intersect_max = other.max intersect_include_max = other.include_max else: intersect_max = self._max intersect_include_max = self._include_max if intersect_min is None and intersect_max is None: return VersionRange() # If the range is just a single version. if intersect_min == intersect_max: # Because we already verified that the lower range isn't strictly # lower, there must be some overlap. assert intersect_include_min and intersect_include_max return intersect_min # If we got here, there is an actual range. return VersionRange(intersect_min, intersect_max, intersect_include_min, intersect_include_max) def union(self, other): # type: (VersionConstraint) -> VersionConstraint from .version import Version if isinstance(other, Version): if self.allows(other): return self if other == self.min: return VersionRange(self.min, self.max, include_min=True, include_max=self.include_max) if other == self.max: return VersionRange(self.min, self.max, include_min=self.include_min, include_max=True) return VersionUnion.of(self, other) if isinstance(other, VersionRange): # If the two ranges don't overlap, we won't be able to create a single # VersionRange for both of them. edges_touch = (self.max == other.min and (self.include_max or other.include_min)) or ( self.min == other.max and (self.include_min or other.include_max) ) if not edges_touch and not self.allows_any(other): return VersionUnion.of(self, other) if self.allows_lower(other): union_min = self.min union_include_min = self.include_min else: union_min = other.min union_include_min = other.include_min if self.allows_higher(other): union_max = self.max union_include_max = self.include_max else: union_max = other.max union_include_max = other.include_max return VersionRange( union_min, union_max, include_min=union_include_min, include_max=union_include_max, ) return VersionUnion.of(self, other) def difference(self, other): # type: (VersionConstraint) -> VersionConstraint from .version import Version if other.is_empty(): return self if isinstance(other, Version): if not self.allows(other): return self if other == self.min: if not self.include_min: return self return VersionRange(self.min, self.max, False, self.include_max) if other == self.max: if not self.include_max: return self return VersionRange(self.min, self.max, self.include_min, False) return VersionUnion.of( VersionRange(self.min, other, self.include_min, False), VersionRange(other, self.max, False, self.include_max), ) elif isinstance(other, VersionRange): if not self.allows_any(other): return self if not self.allows_lower(other): before = None elif self.min == other.min: before = self.min else: before = VersionRange(self.min, other.min, self.include_min, not other.include_min) if not self.allows_higher(other): after = None elif self.max == other.max: after = self.max else: after = VersionRange(other.max, self.max, not other.include_max, self.include_max) if before is None and after is None: return EmptyConstraint() if before is None: return after if after is None: return before return VersionUnion.of(before, after) elif isinstance(other, VersionUnion): ranges = [] # type: List[VersionRange] current = self for range in other.ranges: # Skip any ranges that are strictly lower than [current]. if range.is_strictly_lower(current): continue # If we reach a range strictly higher than [current], no more ranges # will be relevant so we can bail early. if range.is_strictly_higher(current): break difference = current.difference(range) if difference.is_empty(): return EmptyConstraint() elif isinstance(difference, VersionUnion): # If [range] split [current] in half, we only need to continue # checking future ranges against the latter half. ranges.append(difference.ranges[0]) current = difference.ranges[-1] else: current = difference if not ranges: return current return VersionUnion.of(*(ranges + [current])) raise ValueError("Unknown VersionConstraint type {}.".format(other)) def allows_lower(self, other): # type: (VersionRange) -> bool if self.min is None: return other.min is not None if other.min is None: return False if self.min < other.min: return True if self.min > other.min: return False return self.include_min and not other.include_min def allows_higher(self, other): # type: (VersionRange) -> bool if self.max is None: return other.max is not None if other.max is None: return False if self.max < other.max: return False if self.max > other.max: return True return self.include_max and not other.include_max def is_strictly_lower(self, other): # type: (VersionRange) -> bool if self.max is None or other.min is None: return False if self.full_max < other.min: return True if self.full_max > other.min: return False return not self.include_max or not other.include_min def is_strictly_higher(self, other): # type: (VersionRange) -> bool return other.is_strictly_lower(self) def is_adjacent_to(self, other): # type: (VersionRange) -> bool if self.max != other.min: return False return self.include_max and not other.include_min or not self.include_max and other.include_min def __eq__(self, other): if not isinstance(other, VersionRange): return False return ( self._min == other.min and self._max == other.max and self._include_min == other.include_min and self._include_max == other.include_max ) def __lt__(self, other): return self._cmp(other) < 0 def __le__(self, other): return self._cmp(other) <= 0 def __gt__(self, other): return self._cmp(other) > 0 def __ge__(self, other): return self._cmp(other) >= 0 def _cmp(self, other): # type: (VersionRange) -> int if self.min is None: if other.min is None: return self._compare_max(other) return -1 elif other.min is None: return 1 result = self.min._cmp(other.min) if result != 0: return result if self.include_min != other.include_min: return -1 if self.include_min else 1 return self._compare_max(other) def _compare_max(self, other): # type: (VersionRange) -> int if self.max is None: if other.max is None: return 0 return 1 elif other.max is None: return -1 result = self.max._cmp(other.max) if result != 0: return result if self.include_max != other.include_max: return 1 if self.include_max else -1 return 0 def __str__(self): text = "" if self.min is not None: text += ">=" if self.include_min else ">" text += self.min.text if self.max is not None: if self.min is not None: text += "," text += "{}{}".format("<=" if self.include_max else "<", self.max.text) if self.min is None and self.max is None: return "*" return text def __repr__(self): return "".format(str(self)) def __hash__(self): return hash((self.min, self.max, self.include_min, self.include_max)) requirements-detector-1.2.2/requirements_detector/poetry_semver/version_union.py000066400000000000000000000173731443205141000306410ustar00rootroot00000000000000from typing import List import semver from .empty_constraint import EmptyConstraint from .version_constraint import VersionConstraint class VersionUnion(VersionConstraint): """ A version constraint representing a union of multiple disjoint version ranges. An instance of this will only be created if the version can't be represented as a non-compound value. """ def __init__(self, *ranges): self._ranges = list(ranges) @property def ranges(self): return self._ranges @classmethod def of(cls, *ranges): from .version_range import VersionRange flattened = [] for constraint in ranges: if constraint.is_empty(): continue if isinstance(constraint, VersionUnion): flattened += constraint.ranges continue flattened.append(constraint) if not flattened: return EmptyConstraint() if any([constraint.is_any() for constraint in flattened]): return VersionRange() # Only allow Versions and VersionRanges here so we can more easily reason # about everything in flattened. _EmptyVersions and VersionUnions are # filtered out above. for constraint in flattened: if isinstance(constraint, VersionRange): continue raise ValueError("Unknown VersionConstraint type {}.".format(constraint)) flattened.sort() merged = [] for constraint in flattened: # Merge this constraint with the previous one, but only if they touch. if not merged or (not merged[-1].allows_any(constraint) and not merged[-1].is_adjacent_to(constraint)): merged.append(constraint) else: merged[-1] = merged[-1].union(constraint) if len(merged) == 1: return merged[0] return VersionUnion(*merged) def is_empty(self): return False def is_any(self): return False def allows(self, version): # type: (semver.Version) -> bool return any([constraint.allows(version) for constraint in self._ranges]) def allows_all(self, other): # type: (VersionConstraint) -> bool our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) our_current_range = next(our_ranges, None) their_current_range = next(their_ranges, None) while our_current_range and their_current_range: if our_current_range.allows_all(their_current_range): their_current_range = next(their_ranges, None) else: our_current_range = next(our_ranges, None) return their_current_range is None def allows_any(self, other): # type: (VersionConstraint) -> bool our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) our_current_range = next(our_ranges, None) their_current_range = next(their_ranges, None) while our_current_range and their_current_range: if our_current_range.allows_any(their_current_range): return True if their_current_range.allows_higher(our_current_range): our_current_range = next(our_ranges, None) else: their_current_range = next(their_ranges, None) return False def intersect(self, other): # type: (VersionConstraint) -> VersionConstraint our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] our_current_range = next(our_ranges, None) their_current_range = next(their_ranges, None) while our_current_range and their_current_range: intersection = our_current_range.intersect(their_current_range) if not intersection.is_empty(): new_ranges.append(intersection) if their_current_range.allows_higher(our_current_range): our_current_range = next(our_ranges, None) else: their_current_range = next(their_ranges, None) return VersionUnion.of(*new_ranges) def union(self, other): # type: (VersionConstraint) -> VersionConstraint return VersionUnion.of(self, other) def difference(self, other): # type: (VersionConstraint) -> VersionConstraint our_ranges = iter(self._ranges) their_ranges = iter(self._ranges_for(other)) new_ranges = [] state = { "current": next(our_ranges, None), "their_range": next(their_ranges, None), } def their_next_range(): state["their_range"] = next(their_ranges, None) if state["their_range"]: return True new_ranges.append(state["current"]) our_current = next(our_ranges, None) while our_current: new_ranges.append(our_current) our_current = next(our_ranges, None) return False def our_next_range(include_current=True): if include_current: new_ranges.append(state["current"]) our_current = next(our_ranges, None) if not our_current: return False state["current"] = our_current return True while True: if state["their_range"] is None: break if state["their_range"].is_strictly_lower(state["current"]): if not their_next_range(): break continue if state["their_range"].is_strictly_higher(state["current"]): if not our_next_range(): break continue difference = state["current"].difference(state["their_range"]) if isinstance(difference, VersionUnion): assert len(difference.ranges) == 2 new_ranges.append(difference.ranges[0]) state["current"] = difference.ranges[-1] if not their_next_range(): break elif difference.is_empty(): if not our_next_range(False): break else: state["current"] = difference if state["current"].allows_higher(state["their_range"]): if not their_next_range(): break else: if not our_next_range(): break if not new_ranges: return EmptyConstraint() if len(new_ranges) == 1: return new_ranges[0] return VersionUnion.of(*new_ranges) def _ranges_for(self, constraint): # type: (VersionConstraint) -> List[semver.VersionRange] from .version_range import VersionRange if constraint.is_empty(): return [] if isinstance(constraint, VersionUnion): return constraint.ranges if isinstance(constraint, VersionRange): return [constraint] raise ValueError("Unknown VersionConstraint type {}".format(constraint)) def _excludes_single_version(self): # type: () -> bool from .version import Version from .version_range import VersionRange return isinstance(VersionRange().difference(self), Version) def __eq__(self, other): if not isinstance(other, VersionUnion): return False return self._ranges == other.ranges def __str__(self): from .version_range import VersionRange if self._excludes_single_version(): return "!={}".format(VersionRange().difference(self)) return " || ".join([str(r) for r in self._ranges]) def __repr__(self): return "".format(str(self)) requirements-detector-1.2.2/requirements_detector/requirement.py000066400000000000000000000126541443205141000253760ustar00rootroot00000000000000""" This module represents the various types of requirement that can be specified for a project. It is somewhat redundant to re-implement here as we could use `pip.req.InstallRequirement`, but that would require depending on pip which is not easy to do since it will usually be installed by the user at a specific version. Additionally, the pip implementation has a lot of extra features that we don't need - we don't expect relative file paths to exist, for example. Note that the parsing here is also intentionally more lenient - it is not our job to validate the requirements list. """ import os import re from pathlib import Path from typing import Optional from urllib import parse from packaging.requirements import Requirement def _is_filepath(req): # this is (probably) a file return os.path.sep in req or req.startswith(".") def _parse_egg_name(url_fragment): """ >>> _parse_egg_name('egg=fish&cake=lala') fish >>> _parse_egg_name('something_spurious') None """ if "=" not in url_fragment: return None parts = parse.parse_qs(url_fragment) if "egg" not in parts: return None return parts["egg"][0] # taking the first value mimics pip's behaviour def _strip_fragment(urlparts): new_urlparts = ( urlparts.scheme, urlparts.netloc, urlparts.path, urlparts.params, urlparts.query, None, ) return parse.urlunparse(new_urlparts) class DetectedRequirement: def __init__( self, name: str = None, url: str = None, requirement: Requirement = None, location_defined: Path = None ): if requirement is not None: self.name = requirement.name self.requirement = requirement self.version_specs = [(s.operator, s.version) for s in requirement.specifier] self.url = None else: self.name = name self.version_specs = [] self.url = url self.requirement = None self.location_defined = location_defined def _format_specs(self) -> str: return ",".join(["%s%s" % (comp, version) for comp, version in self.version_specs]) def pip_format(self) -> str: if self.url: if self.name: return "%s#egg=%s" % (self.url, self.name) return self.url if self.name: if self.version_specs: return "%s%s" % (self.name, self._format_specs()) return self.name raise ValueError(f"Cannot convert {self} to pip format, no name or URL") def __str__(self): rep = self.name or "Unknown" if self.version_specs: specs = ",".join(["%s%s" % (comp, ver) for comp, ver in self.version_specs]) rep = "%s%s" % (rep, specs) if self.url: rep = "%s (%s)" % (rep, self.url) return rep def __hash__(self): return hash(str(self.name) + str(self.url) + str(self.version_specs)) def __repr__(self): return "" % str(self) def __eq__(self, other): return self.name == other.name and self.url == other.url and self.version_specs == other.version_specs def __gt__(self, other): return (self.name or "") > (other.name or "") @staticmethod def parse(line, location_defined: Path = None) -> Optional["DetectedRequirement"]: # the options for a Pip requirements file are: # # 1) # 2) # 3) (#egg=)? # 4) (#egg=)? # 5) # 6) (-e|--editable) (#egg=#egg= line = line.strip() if line.startswith("--hash=sha256:"): # skip multi-line shas, produced by poetry export return None # We need to match whitespace + # because url based requirements specify # egg_name after a '#' comment_pos = re.search(r"\s#", line) if comment_pos: line = line[: comment_pos.start()] # strip the editable flag line = re.sub("^(-e|--editable) ", "", line) # remove the python version stuff from poetry files line = line.split(";")[0] url = parse.urlparse(line) # if it is a VCS URL, then we want to strip off the protocol as urlparse # might not handle it correctly vcs_scheme = None if "+" in url.scheme or url.scheme in ("git",): if url.scheme == "git": vcs_scheme = "git+git" else: vcs_scheme = url.scheme url = parse.urlparse(re.sub(r"^%s://" % re.escape(url.scheme), "", line)) if vcs_scheme is None and url.scheme == "" and not _is_filepath(line): # if we are here, it is a simple dependency try: req = Requirement(line) except ValueError: # this happens if the line is invalid return None else: return DetectedRequirement(requirement=req, location_defined=location_defined) # otherwise, this is some kind of URL name = _parse_egg_name(url.fragment) url = _strip_fragment(url) if vcs_scheme: url = "%s://%s" % (vcs_scheme, url) return DetectedRequirement(name=name, url=url, location_defined=location_defined) requirements-detector-1.2.2/requirements_detector/run.py000066400000000000000000000015351443205141000236360ustar00rootroot00000000000000import os import sys from pathlib import Path from typing import NoReturn from . import find_requirements from .exceptions import RequirementsNotFound from .formatters import FORMATTERS def _die(message) -> NoReturn: sys.stderr.write("%s\n" % message) sys.exit(1) def run() -> NoReturn: if len(sys.argv) > 1: path = Path(sys.argv[1]) else: path = Path.cwd() if not path.exists(): _die("%s does not exist" % path) if not path.is_dir(): _die("%s is not a directory" % path) try: requirements = find_requirements(path) except RequirementsNotFound: _die("Unable to find requirements at %s" % path) format_name = "requirements_file" # TODO: other output formats such as JSON FORMATTERS[format_name](requirements) sys.exit(0) if __name__ == "__main__": run() requirements-detector-1.2.2/tests/000077500000000000000000000000001443205141000172025ustar00rootroot00000000000000requirements-detector-1.2.2/tests/__init__.py000066400000000000000000000000001443205141000213010ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/000077500000000000000000000000001443205141000211605ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/syntax_error/000077500000000000000000000000001443205141000237175ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/syntax_error/regular_indentation.py000066400000000000000000000003511443205141000303250ustar00rootroot00000000000000# -*- coding: UTF-8 -*- from distutils.core import setup if foo: # just for a test with indentation bar() setup(name="prospector-test-4-üéø", version="0.0.1", install_requires=["Django==1.5.0", "django-gubbins==1.1.2"]) requirements-detector-1.2.2/tests/detection/syntax_error/setup.py000066400000000000000000000003121443205141000254250ustar00rootroot00000000000000# noqa from distutils.core import setup setup( name='prospector-test-1', version='0.0.1', install_requires=[ 'Django==1.5.0', 'django-gubbins==1.1.2' ] narm narm narm ) requirements-detector-1.2.2/tests/detection/syntax_error/setup_multiline_string.py000066400000000000000000000003641443205141000311040ustar00rootroot00000000000000from distutils.core import setup comment = 'this is a long comment ' \ 'on two lines' setup( name='prospector-test-1', version='0.0.1', install_requires=[ 'Django==1.5.0', 'django-gubbins==1.1.2' ] ) requirements-detector-1.2.2/tests/detection/test1/000077500000000000000000000000001443205141000222205ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test1/requirements.txt000066400000000000000000000001551443205141000255050ustar00rootroot00000000000000-i https://example.com/custom/pypi Django>=1.5.0 South==0.8.2 amqp!=1.0.13 # we want six too six<1.4,>=1.3.0 requirements-detector-1.2.2/tests/detection/test2/000077500000000000000000000000001443205141000222215ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test2/requirements/000077500000000000000000000000001443205141000247445ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test2/requirements/base.txt000066400000000000000000000000341443205141000264140ustar00rootroot00000000000000amqp==1.0.13 anyjson==0.3.3 requirements-detector-1.2.2/tests/detection/test2/requirements/webui.pip000066400000000000000000000000331443205141000265650ustar00rootroot00000000000000Django==1.5.2 South==0.8.2 requirements-detector-1.2.2/tests/detection/test3/000077500000000000000000000000001443205141000222225ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test3/pip_requirements.txt000066400000000000000000000000171443205141000263540ustar00rootroot00000000000000anyjson==0.3.3 requirements-detector-1.2.2/tests/detection/test3/reqs.txt000066400000000000000000000000261443205141000237330ustar00rootroot00000000000000django-gubbins==1.1.2 requirements-detector-1.2.2/tests/detection/test3/requirements_base.txt000066400000000000000000000000151443205141000264740ustar00rootroot00000000000000amqp==1.0.13 requirements-detector-1.2.2/tests/detection/test3/requirements_test.txt000066400000000000000000000000151443205141000265410ustar00rootroot00000000000000South==0.8.2 requirements-detector-1.2.2/tests/detection/test3/test_requirements.txt000066400000000000000000000000161443205141000265420ustar00rootroot00000000000000Django==1.5.2 requirements-detector-1.2.2/tests/detection/test4/000077500000000000000000000000001443205141000222235ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test4/callable.py000066400000000000000000000003131443205141000243310ustar00rootroot00000000000000from distutils.core import setup def _install_requires(): return ["Django==1.5.0", "django-gubbins==1.1.2"] setup(name="prospector-test-2", version="0.0.1", install_requires=_install_requires()) requirements-detector-1.2.2/tests/detection/test4/in_file.py000066400000000000000000000002671443205141000242070ustar00rootroot00000000000000from distutils.core import setup _install_requires = ["Django==1.5.0", "django-gubbins==1.1.2"] setup(name="prospector-test-2", version="0.0.1", install_requires=_install_requires) requirements-detector-1.2.2/tests/detection/test4/simple.py000066400000000000000000000002201443205141000240600ustar00rootroot00000000000000from distutils.core import setup setup(name="prospector-test-1", version="0.0.1", install_requires=["Django==1.5.0", "django-gubbins==1.1.2"]) requirements-detector-1.2.2/tests/detection/test4/subscript_assign.py000066400000000000000000000005761443205141000261670ustar00rootroot00000000000000""" This test is to verify that top level subscript assigns (x[y]) don't break the parser. For version <=0.1, a subscript assign would break the setup.py AST walker completely. """ from distutils.core import setup something = dict() something["fish"] = ["a", "b", "c"] setup(name="prospector-test-1", version="0.0.1", install_requires=("Django==1.5.0", "django-gubbins==1.1.2")) requirements-detector-1.2.2/tests/detection/test4/tuple.py000066400000000000000000000002201443205141000237200ustar00rootroot00000000000000from distutils.core import setup setup(name="prospector-test-1", version="0.0.1", install_requires=("Django==1.5.0", "django-gubbins==1.1.2")) requirements-detector-1.2.2/tests/detection/test4/uses_requires.py000066400000000000000000000002101443205141000254640ustar00rootroot00000000000000from distutils.core import setup setup(name="prospector-test-1", version="0.0.1", requires=["Django==1.5.0", "django-gubbins==1.1.2"]) requirements-detector-1.2.2/tests/detection/test4/uses_requires_and_install_requires.py000066400000000000000000000002751443205141000317660ustar00rootroot00000000000000from distutils.core import setup setup( name="prospector-test-1", version="0.0.1", requires=[ "Django==1.5.0", ], install_requires=["django-gubbins==1.1.2"], ) requirements-detector-1.2.2/tests/detection/test4/utf8.py000066400000000000000000000002571443205141000234670ustar00rootroot00000000000000# -*- coding: UTF-8 -*- from distutils.core import setup setup(name="prospector-test-4-üéø", version="0.0.1", install_requires=["Django==1.5.0", "django-gubbins==1.1.2"]) requirements-detector-1.2.2/tests/detection/test5/000077500000000000000000000000001443205141000222245ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test5/invalid_requirements.txt000066400000000000000000000000571443205141000272200ustar00rootroot00000000000000<<<<<<< HEAD django<1.6 ======= django >>>>>>> requirements-detector-1.2.2/tests/detection/test6/000077500000000000000000000000001443205141000222255ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test6/requirements.txt000066400000000000000000000022141443205141000255100ustar00rootroot00000000000000# core framework uwsgi flask # flask extensions flask-mail flask-migrate flask-oauthlib flask-script flask-scrypt flask-weasyprint flask-sqlalchemy #flask-testing git+git://github.com/jarus/flask-testing.git flask-jwt flask-bouncer flask-restful flask-marshmallow flask-assets flask-collect awesome-slugify # testing #fake-factory https://github.com/traumtopf/faker/tarball/master surrealism freezegun selenium testtools # maybe extensions #restless #flash-babel #flask-restless #flask-assets #flask-cache #flask-classy #flask-debugtoolbar #flask-googlemaps #flask-gravatar #flask-jinjahelpers #flask-lesscss #flask-markdown #flask-moment #flask-principal #flask-restdoc #flask-runner #flask-user # other extensions #wtforms-alchemy #sqlalchemy-defaults #wtforms-components #wtforms-json # testing #coverage # correctness #pyflakes #pep8 #flake8 #pylint prospector # other stuff paypalrestsdk #outputty pycountry #certifi psycopg2 #raven[flask] # client for sentry (a web application error logger) git+https://github.com/lazzrek/raven-python.git#egg=raven[flask] sadisplay==0.3.8dev isodate colour-runner requests pygeoip # webassets filters rjsmin cssmin requirements-detector-1.2.2/tests/detection/test7/000077500000000000000000000000001443205141000222265ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test7/poetry-format-requirements.txt000066400000000000000000000222631443205141000303250ustar00rootroot00000000000000astroid==1.6.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0") \ --hash=sha256:87de48a92e29cedf7210ffa853d11441e7ad94cb47bacd91b023499b51cbc756 \ --hash=sha256:d25869fc7f44f1d9fb7d24fd7ea0639656f5355fc3089cd1f3d18c6ec6b124c7 lazy-object-proxy==1.7.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" \ --hash=sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4 \ --hash=sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b \ --hash=sha256:a57d51ed2997e97f3b8e3500c984db50a554bb5db56c50b5dab1b41339b37e36 \ --hash=sha256:fd45683c3caddf83abbb1249b653a266e7069a09f486daa8863fb0e7496a9fdb \ --hash=sha256:8561da8b3dd22d696244d6d0d5330618c993a215070f473b699e00cf1f3f6443 \ --hash=sha256:fccdf7c2c5821a8cbd0a9440a456f5050492f2270bd54e94360cac663398739b \ --hash=sha256:898322f8d078f2654d275124a8dd19b079080ae977033b713f677afcfc88e2b9 \ --hash=sha256:85b232e791f2229a4f55840ed54706110c80c0a210d076eee093f2b2e33e1bfd \ --hash=sha256:46ff647e76f106bb444b4533bb4153c7370cdf52efc62ccfc1a28bdb3cc95442 \ --hash=sha256:12f3bb77efe1367b2515f8cb4790a11cffae889148ad33adad07b9b55e0ab22c \ --hash=sha256:c19814163728941bb871240d45c4c30d33b8a2e85972c44d4e63dd7107faba44 \ --hash=sha256:e40f2013d96d30217a51eeb1db28c9ac41e9d0ee915ef9d00da639c5b63f01a1 \ --hash=sha256:2052837718516a94940867e16b1bb10edb069ab475c3ad84fd1e1a6dd2c0fcfc \ --hash=sha256:6a24357267aa976abab660b1d47a34aaf07259a0c3859a34e536f1ee6e76b5bb \ --hash=sha256:6aff3fe5de0831867092e017cf67e2750c6a1c7d88d84d2481bd84a2e019ec35 \ --hash=sha256:6a6e94c7b02641d1311228a102607ecd576f70734dc3d5e22610111aeacba8a0 \ --hash=sha256:c4ce15276a1a14549d7e81c243b887293904ad2d94ad767f42df91e75fd7b5b6 \ --hash=sha256:e368b7f7eac182a59ff1f81d5f3802161932a41dc1b1cc45c1f757dc876b5d2c \ --hash=sha256:6ecbb350991d6434e1388bee761ece3260e5228952b1f0c46ffc800eb313ff42 \ --hash=sha256:553b0f0d8dbf21890dd66edd771f9b1b5f51bd912fa5f26de4449bfc5af5e029 \ --hash=sha256:c7a683c37a8a24f6428c28c561c80d5f4fd316ddcf0c7cab999b15ab3f5c5c69 \ --hash=sha256:df2631f9d67259dc9620d831384ed7732a198eb434eadf69aea95ad18c587a28 \ --hash=sha256:07fa44286cda977bd4803b656ffc1c9b7e3bc7dff7d34263446aec8f8c96f88a \ --hash=sha256:4dca6244e4121c74cc20542c2ca39e5c4a5027c81d112bfb893cf0790f96f57e \ --hash=sha256:91ba172fc5b03978764d1df5144b4ba4ab13290d7bab7a50f12d8117f8630c38 \ --hash=sha256:043651b6cb706eee4f91854da4a089816a6606c1428fd391573ef8cb642ae4f7 \ --hash=sha256:b9e89b87c707dd769c4ea91f7a31538888aad05c116a59820f28d59b3ebfe25a \ --hash=sha256:9d166602b525bf54ac994cf833c385bfcc341b364e3ee71e3bf5a1336e677b55 \ --hash=sha256:8f3953eb575b45480db6568306893f0bd9d8dfeeebd46812aa09ca9579595148 \ --hash=sha256:dd7ed7429dbb6c494aa9bc4e09d94b778a3579be699f9d67da7e6804c422d3de \ --hash=sha256:70ed0c2b380eb6248abdef3cd425fc52f0abd92d2b07ce26359fcbc399f636ad \ --hash=sha256:7096a5e0c1115ec82641afbdd70451a144558ea5cf564a896294e346eb611be1 \ --hash=sha256:f769457a639403073968d118bc70110e7dce294688009f5c24ab78800ae56dc8 \ --hash=sha256:39b0e26725c5023757fc1ab2a89ef9d7ab23b84f9251e28f9cc114d5b59c1b09 \ --hash=sha256:2130db8ed69a48a3440103d4a520b89d8a9405f1b06e2cc81640509e8bf6548f \ --hash=sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61 \ --hash=sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84 six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" \ --hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 \ --hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 wrapt==1.14.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" \ --hash=sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7 \ --hash=sha256:9a3ff5fb015f6feb78340143584d9f8a0b91b6293d6b5cf4295b3e95d179b88c \ --hash=sha256:4b847029e2d5e11fd536c9ac3136ddc3f54bc9488a75ef7d040a3900406a91eb \ --hash=sha256:9a5a544861b21e0e7575b6023adebe7a8c6321127bb1d238eb40d99803a0e8bd \ --hash=sha256:88236b90dda77f0394f878324cfbae05ae6fde8a84d548cfe73a75278d760291 \ --hash=sha256:f0408e2dbad9e82b4c960274214af533f856a199c9274bd4aff55d4634dedc33 \ --hash=sha256:9d8c68c4145041b4eeae96239802cfdfd9ef927754a5be3f50505f09f309d8c6 \ --hash=sha256:22626dca56fd7f55a0733e604f1027277eb0f4f3d95ff28f15d27ac25a45f71b \ --hash=sha256:65bf3eb34721bf18b5a021a1ad7aa05947a1767d1aa272b725728014475ea7d5 \ --hash=sha256:09d16ae7a13cff43660155383a2372b4aa09109c7127aa3f24c3cf99b891c330 \ --hash=sha256:debaf04f813ada978d7d16c7dfa16f3c9c2ec9adf4656efdc4defdf841fc2f0c \ --hash=sha256:748df39ed634851350efa87690c2237a678ed794fe9ede3f0d79f071ee042561 \ --hash=sha256:1807054aa7b61ad8d8103b3b30c9764de2e9d0c0978e9d3fc337e4e74bf25faa \ --hash=sha256:763a73ab377390e2af26042f685a26787c402390f682443727b847e9496e4a2a \ --hash=sha256:8529b07b49b2d89d6917cfa157d3ea1dfb4d319d51e23030664a827fe5fd2131 \ --hash=sha256:68aeefac31c1f73949662ba8affaf9950b9938b712fb9d428fa2a07e40ee57f8 \ --hash=sha256:59d7d92cee84a547d91267f0fea381c363121d70fe90b12cd88241bd9b0e1763 \ --hash=sha256:3a88254881e8a8c4784ecc9cb2249ff757fd94b911d5df9a5984961b96113fff \ --hash=sha256:9a242871b3d8eecc56d350e5e03ea1854de47b17f040446da0e47dc3e0b9ad4d \ --hash=sha256:a65bffd24409454b889af33b6c49d0d9bcd1a219b972fba975ac935f17bdf627 \ --hash=sha256:9d9fcd06c952efa4b6b95f3d788a819b7f33d11bea377be6b8980c95e7d10775 \ --hash=sha256:db6a0ddc1282ceb9032e41853e659c9b638789be38e5b8ad7498caac00231c23 \ --hash=sha256:14e7e2c5f5fca67e9a6d5f753d21f138398cad2b1159913ec9e9a67745f09ba3 \ --hash=sha256:6d9810d4f697d58fd66039ab959e6d37e63ab377008ef1d63904df25956c7db0 \ --hash=sha256:d808a5a5411982a09fef6b49aac62986274ab050e9d3e9817ad65b2791ed1425 \ --hash=sha256:b77159d9862374da213f741af0c361720200ab7ad21b9f12556e0eb95912cd48 \ --hash=sha256:36a76a7527df8583112b24adc01748cd51a2d14e905b337a6fefa8b96fc708fb \ --hash=sha256:a0057b5435a65b933cbf5d859cd4956624df37b8bf0917c71756e4b3d9958b9e \ --hash=sha256:3a0a4ca02752ced5f37498827e49c414d694ad7cf451ee850e3ff160f2bee9d3 \ --hash=sha256:8c6be72eac3c14baa473620e04f74186c5d8f45d80f8f2b4eda6e1d18af808e8 \ --hash=sha256:21b1106bff6ece8cb203ef45b4f5778d7226c941c83aaaa1e1f0f4f32cc148cd \ --hash=sha256:493da1f8b1bb8a623c16552fb4a1e164c0200447eb83d3f68b44315ead3f9036 \ --hash=sha256:89ba3d548ee1e6291a20f3c7380c92f71e358ce8b9e48161401e087e0bc740f8 \ --hash=sha256:729d5e96566f44fccac6c4447ec2332636b4fe273f03da128fff8d5559782b06 \ --hash=sha256:891c353e95bb11abb548ca95c8b98050f3620a7378332eb90d6acdef35b401d4 \ --hash=sha256:23f96134a3aa24cc50614920cc087e22f87439053d886e474638c68c8d15dc80 \ --hash=sha256:6807bcee549a8cb2f38f73f469703a1d8d5d990815c3004f21ddb68a567385ce \ --hash=sha256:6915682f9a9bc4cf2908e83caf5895a685da1fbd20b6d485dafb8e218a338279 \ --hash=sha256:f2f3bc7cd9c9fcd39143f11342eb5963317bd54ecc98e3650ca22704b69d9653 \ --hash=sha256:3a71dbd792cc7a3d772ef8cd08d3048593f13d6f40a11f3427c000cf0a5b36a0 \ --hash=sha256:5a0898a640559dec00f3614ffb11d97a2666ee9a2a6bad1259c9facd01a1d4d9 \ --hash=sha256:167e4793dc987f77fd476862d32fa404d42b71f6a85d3b38cbce711dba5e6b68 \ --hash=sha256:d066ffc5ed0be00cd0352c95800a519cf9e4b5dd34a028d301bdc7177c72daf3 \ --hash=sha256:d9bdfa74d369256e4218000a629978590fd7cb6cf6893251dad13d051090436d \ --hash=sha256:2498762814dd7dd2a1d0248eda2afbc3dd9c11537bc8200a4b21789b6df6cd38 \ --hash=sha256:5f24ca7953f2643d59a9c87d6e272d8adddd4a53bb62b9208f36db408d7aafc7 \ --hash=sha256:5b835b86bd5a1bdbe257d610eecab07bf685b1af2a7563093e0e69180c1d4af1 \ --hash=sha256:b21650fa6907e523869e0396c5bd591cc326e5c1dd594dcdccac089561cacfb8 \ --hash=sha256:354d9fc6b1e44750e2a67b4b108841f5f5ea08853453ecbf44c81fdc2e0d50bd \ --hash=sha256:1f83e9c21cd5275991076b2ba1cd35418af3504667affb4745b48937e214bafe \ --hash=sha256:61e1a064906ccba038aa3c4a5a82f6199749efbbb3cef0804ae5c37f550eded0 \ --hash=sha256:28c659878f684365d53cf59dc9a1929ea2eecd7ac65da762be8b1ba193f7e84f \ --hash=sha256:b0ed6ad6c9640671689c2dbe6244680fe8b897c08fd1fab2228429b66c518e5e \ --hash=sha256:b3f7e671fb19734c872566e57ce7fc235fa953d7c181bb4ef138e17d607dc8a1 \ --hash=sha256:87fa943e8bbe40c8c1ba4086971a6fefbf75e9991217c55ed1bcb2f1985bd3d4 \ --hash=sha256:4775a574e9d84e0212f5b18886cace049a42e13e12009bb0491562a48bb2b758 \ --hash=sha256:9d57677238a0c5411c76097b8b93bdebb02eb845814c90f0b01727527a179e4d \ --hash=sha256:00108411e0f34c52ce16f81f1d308a571df7784932cc7491d1e94be2ee93374b \ --hash=sha256:d332eecf307fca852d02b63f35a7872de32d5ba8b4ec32da82f45df986b39ff6 \ --hash=sha256:01f799def9b96a8ec1ef6b9c1bbaf2bbc859b87545efbecc4a78faea13d0e3a0 \ --hash=sha256:47045ed35481e857918ae78b54891fac0c1d197f22c95778e66302668309336c \ --hash=sha256:2eca15d6b947cfff51ed76b2d60fd172c6ecd418ddab1c5126032d27f74bc350 \ --hash=sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc \ --hash=sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311 requirements-detector-1.2.2/tests/detection/test8/000077500000000000000000000000001443205141000222275ustar00rootroot00000000000000requirements-detector-1.2.2/tests/detection/test8/pyproject.toml000066400000000000000000000007641443205141000251520ustar00rootroot00000000000000[tool.poetry] name = "test8" version = "0.1.0" description = "" authors = ["Nobody Smith "] license = "MIT" [tool.poetry.dependencies] python = "^3.3" requests = ">=2.25,<2.27" twine = "~3.8" click = "==8.0.4" pyroma = {version = ">=2.4", optional = true} mytestdependency = {git = "ssh://git@github.com/MyOrg/MyRepo.git",rev = "1.2.3"} [tool.poetry.dev-dependencies] pytest = "^6.2.4" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" requirements-detector-1.2.2/tests/test_detection.py000066400000000000000000000105631443205141000225760ustar00rootroot00000000000000from pathlib import Path from unittest import TestCase from requirements_detector.detect import ( CouldNotParseRequirements, from_pyproject_toml, from_requirements_blob, from_requirements_dir, from_requirements_txt, from_setup_py, ) from requirements_detector.requirement import DetectedRequirement _TEST_DIR = Path(__file__).parent / 'detection' class DependencyDetectionTest(TestCase): def _expected(self, *requirements): return sorted(DetectedRequirement.parse(req) for req in requirements) def test_requirements_txt_parsing(self): filepath = _TEST_DIR / 'test1/requirements.txt' dependencies = from_requirements_txt(filepath) expected = self._expected( "amqp!=1.0.13", "Django>=1.5.0", "six<1.4,>=1.3.0", "South==0.8.2", ) self.assertEqual(expected, sorted(dependencies)) def test_requirements_dir_parsing(self): filepath = _TEST_DIR / "test2/requirements" dependencies = from_requirements_dir(filepath) expected = self._expected( "amqp==1.0.13", "anyjson==0.3.3", "Django==1.5.2", "South==0.8.2", ) self.assertEqual(expected, sorted(dependencies)) def test_requirements_blob_parsing(self): filepath = _TEST_DIR / "test3" dependencies = from_requirements_blob(filepath) expected = self._expected( "amqp==1.0.13", "anyjson==0.3.3", "django-gubbins==1.1.2", ) self.assertEqual(expected, sorted(dependencies)) def test_invalid_requirements_txt(self): filepath = _TEST_DIR / "test5/invalid_requirements.txt" dependencies = from_requirements_txt(filepath) expected = self._expected("django<1.6", "django") self.assertEqual(expected, sorted(dependencies)) def test_requirements_txt(self): filepath = _TEST_DIR / "test6/requirements.txt" reqs = from_requirements_txt(filepath) self.assertTrue(len(reqs) > 0) def test_poetry_requirements_txt(self): # tests parsing a requirements format generated by 'poetry export' filepath = _TEST_DIR / "test7/poetry-format-requirements.txt" reqs = from_requirements_txt(str(filepath)) self.assertTrue(len(reqs) > 0) def test_pyproject_toml(self): # tests parsing a pyproject.toml file filepath = _TEST_DIR / "test8/pyproject.toml" reqs = from_pyproject_toml(str(filepath)) self.assertTrue(len(reqs) > 0) names = [req.name for req in reqs] self.assertIn('click', names) self.assertIn('twine', names) self.assertIn('requests', names) self.assertIn('pytest', names) pyroma = [req for req in reqs if req.name == 'pyroma'][0] self.assertEqual(2, len(pyroma.version_specs[0])) self.assertEqual('pyroma>=2.4', str(pyroma)) def _test_setup_py(self, setup_py_file, *expected): filepath = _TEST_DIR / 'test4' / setup_py_file dependencies = from_setup_py(str(filepath)) expected = self._expected(*expected) self.assertEqual(expected, sorted(dependencies)) def _test_setup_py_not_parseable(self, setup_py_file): filepath = _TEST_DIR / 'test4' / setup_py_file self.assertRaises(CouldNotParseRequirements, from_setup_py, filepath) def test_simple_setup_py_parsing(self): self._test_setup_py("simple.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_setup_py_reqs_defined_in_file_parsing(self): self._test_setup_py("in_file.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_setup_py_tuple(self): self._test_setup_py("tuple.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_subscript_assign(self): self._test_setup_py("subscript_assign.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_utf8_setup_py(self): self._test_setup_py("utf8.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_requires_setup_py(self): self._test_setup_py("uses_requires.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_requires_and_install_requires_setup_py(self): self._test_setup_py("uses_requires_and_install_requires.py", "Django==1.5.0", "django-gubbins==1.1.2") def test_callable_install_requires(self): self._test_setup_py_not_parseable("callable.py") requirements-detector-1.2.2/tests/test_failure_cases.py000066400000000000000000000011121443205141000234130ustar00rootroot00000000000000import os from pathlib import Path import pytest from requirements_detector.detect import CouldNotParseRequirements, from_setup_py _TEST_DIR = Path(__file__).parent / 'detection/syntax_error' def test_setup_py_syntax_error(): filepath = _TEST_DIR / "setup.py" with pytest.raises(CouldNotParseRequirements): from_setup_py(filepath) def test_setup_py_multiline_string(): filepath = _TEST_DIR / "setup_multiline_string.py" from_setup_py(filepath) def test_regular_indentation(): filepath = _TEST_DIR / "regular_indentation.py" from_setup_py(filepath) requirements-detector-1.2.2/tests/test_parsing.py000066400000000000000000000072711443205141000222650ustar00rootroot00000000000000from unittest import TestCase from urllib import parse as urlparse from requirements_detector.requirement import ( DetectedRequirement, _parse_egg_name, _strip_fragment, ) class TestRequirementParsing(TestCase): def _test(self, requirement, name=None, version_specs=None, url=None): req = DetectedRequirement.parse(requirement) self.assertEqual(name, req.name) if version_specs is None: self.assertEqual([], req.version_specs) else: for spec in version_specs: self.assertTrue(spec in req.version_specs) self.assertEqual(url, req.url) def test_basic_requirement(self): self._test("Django", "Django") self._test("celery", "celery") def test_requirement_with_versions(self): self._test("Django==1.5.2", "Django", [("==", "1.5.2")]) self._test("South>0.8", "South", [(">", "0.8")]) self._test("django-gubbins!=1.1.1,>1.1", "django-gubbins", [("!=", "1.1.1"), (">", "1.1")]) def test_relative_file_path(self): self._test("../somelib", url="../somelib") def test_vcs_url(self): self._test( "git+ssh://git@github.com/something/somelib.git", url="git+ssh://git@github.com/something/somelib.git" ) self._test( "git+ssh://git@github.com/something/somelib.git#egg=somelib", name="somelib", url="git+ssh://git@github.com/something/somelib.git", ) self._test( "git://github.com/peeb/django-mollie-ideal.git#egg=mollie", name="mollie", url="git+git://github.com/peeb/django-mollie-ideal.git", ) def test_archive_url(self): self._test("http://example.com/somelib.tar.gz", url="http://example.com/somelib.tar.gz") self._test( "http://example.com/somelib.tar.gz#egg=somelib", name="somelib", url="http://example.com/somelib.tar.gz" ) def test_editable_relative_path(self): self._test("-e ../somelib", url="../somelib") def test_editable_vcs_url(self): self._test( "--editable git+ssh://git@github.com/something/somelib.git#egg=somelib", name="somelib", url="git+ssh://git@github.com/something/somelib.git", ) def test_comments(self): self._test("celery == 0.1 # comment", "celery", [("==", "0.1")]) self._test("celery == 0.1\t# comment", "celery", [("==", "0.1")]) self._test( "somelib == 0.15 # pinned to 0.15 (https://github.com/owner/repo/issues/111)", "somelib", [("==", "0.15")] ) self._test("http://example.com/somelib.tar.gz # comment", url="http://example.com/somelib.tar.gz") self._test( "http://example.com/somelib.tar.gz#egg=somelib # url comment http://foo.com/bar", name="somelib", url="http://example.com/somelib.tar.gz", ) class TestEggFragmentParsing(TestCase): def test_simple(self): self.assertEqual("somelib", _parse_egg_name("egg=somelib")) def test_no_egg_value(self): self.assertTrue(_parse_egg_name("a=b&c=2") is None) def test_no_pairs(self): self.assertTrue(_parse_egg_name("somelib") is None) def test_first_egg_val(self): self.assertEqual("somelib", _parse_egg_name("egg=somelib&egg=anotherlib")) def test_multiple_fragment_values(self): self.assertEqual("somelib", _parse_egg_name("a=1&egg=somelib&b=2")) class TestFragmentStripping(TestCase): def test_stripping(self): url = "http://example.com/index.html?a=b&c=2#some_fragment" parts = urlparse.urlparse(url) self.assertEqual("http://example.com/index.html?a=b&c=2", _strip_fragment(parts)) requirements-detector-1.2.2/tests/test_poetry_semver/000077500000000000000000000000001443205141000231445ustar00rootroot00000000000000requirements-detector-1.2.2/tests/test_poetry_semver/__init__.py000066400000000000000000000000001443205141000252430ustar00rootroot00000000000000requirements-detector-1.2.2/tests/test_poetry_semver/test_main.py000066400000000000000000000155221443205141000255060ustar00rootroot00000000000000import pytest from requirements_detector.poetry_semver import ( Version, VersionRange, VersionUnion, parse_constraint, ) @pytest.mark.parametrize( "input,constraint", [ ("*", VersionRange()), ("*.*", VersionRange()), ("v*.*", VersionRange()), ("*.x.*", VersionRange()), ("x.X.x.*", VersionRange()), # ('!=1.0.0', Constraint('!=', '1.0.0.0')), (">1.0.0", VersionRange(min=Version(1, 0, 0))), ("<1.2.3", VersionRange(max=Version(1, 2, 3))), ("<=1.2.3", VersionRange(max=Version(1, 2, 3), include_max=True)), (">=1.2.3", VersionRange(min=Version(1, 2, 3), include_min=True)), ("=1.2.3", Version(1, 2, 3)), ("1.2.3", Version(1, 2, 3)), ("=1.0", Version(1, 0, 0)), ("1.2.3b5", Version(1, 2, 3, pre="b5")), (">= 1.2.3", VersionRange(min=Version(1, 2, 3), include_min=True)), (">dev", VersionRange(min=Version(0, 0, pre="dev"))), # Issue 206 ], ) def test_parse_constraint(input, constraint): assert parse_constraint(input) == constraint @pytest.mark.parametrize( "input,constraint", [ ("v2.*", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)), ("2.*.*", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)), ("20.*", VersionRange(Version(20, 0, 0), Version(21, 0, 0), True)), ("20.*.*", VersionRange(Version(20, 0, 0), Version(21, 0, 0), True)), ("2.0.*", VersionRange(Version(2, 0, 0), Version(2, 1, 0), True)), ("2.x", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)), ("2.x.x", VersionRange(Version(2, 0, 0), Version(3, 0, 0), True)), ("2.2.X", VersionRange(Version(2, 2, 0), Version(2, 3, 0), True)), ("0.*", VersionRange(max=Version(1, 0, 0))), ("0.*.*", VersionRange(max=Version(1, 0, 0))), ("0.x", VersionRange(max=Version(1, 0, 0))), ], ) def test_parse_constraint_wildcard(input, constraint): assert parse_constraint(input) == constraint @pytest.mark.parametrize( "input,constraint", [ ("~v1", VersionRange(Version(1, 0, 0), Version(2, 0, 0), True)), ("~1.0", VersionRange(Version(1, 0, 0), Version(1, 1, 0), True)), ("~1.0.0", VersionRange(Version(1, 0, 0), Version(1, 1, 0), True)), ("~1.2", VersionRange(Version(1, 2, 0), Version(1, 3, 0), True)), ("~1.2.3", VersionRange(Version(1, 2, 3), Version(1, 3, 0), True)), ( "~1.2-beta", VersionRange(Version(1, 2, 0, pre="beta"), Version(1, 3, 0), True), ), ("~1.2-b2", VersionRange(Version(1, 2, 0, pre="b2"), Version(1, 3, 0), True)), ("~0.3", VersionRange(Version(0, 3, 0), Version(0, 4, 0), True)), ("~3.5", VersionRange(Version(3, 5, 0), Version(3, 6, 0), True)), ("~=3.5", VersionRange(Version(3, 5, 0), Version(4, 0, 0), True)), # PEP 440 ("~=3.5.3", VersionRange(Version(3, 5, 3), Version(3, 6, 0), True)), # PEP 440 ], ) def test_parse_constraint_tilde(input, constraint): assert parse_constraint(input) == constraint @pytest.mark.parametrize( "input,constraint", [ ("^v1", VersionRange(Version(1, 0, 0), Version(2, 0, 0), True)), ("^0", VersionRange(Version(0, 0, 0), Version(1, 0, 0), True)), ("^0.0", VersionRange(Version(0, 0, 0), Version(0, 1, 0), True)), ("^1.2", VersionRange(Version(1, 2, 0), Version(2, 0, 0), True)), ( "^1.2.3-beta.2", VersionRange(Version(1, 2, 3, pre="beta.2"), Version(2, 0, 0), True), ), ("^1.2.3", VersionRange(Version(1, 2, 3), Version(2, 0, 0), True)), ("^0.2.3", VersionRange(Version(0, 2, 3), Version(0, 3, 0), True)), ("^0.2", VersionRange(Version(0, 2, 0), Version(0, 3, 0), True)), ("^0.2.0", VersionRange(Version(0, 2, 0), Version(0, 3, 0), True)), ("^0.0.3", VersionRange(Version(0, 0, 3), Version(0, 0, 4), True)), ], ) def test_parse_constraint_caret(input, constraint): assert parse_constraint(input) == constraint @pytest.mark.parametrize( "input", [ ">2.0,<=3.0", ">2.0 <=3.0", ">2.0 <=3.0", ">2.0, <=3.0", ">2.0 ,<=3.0", ">2.0 , <=3.0", ">2.0 , <=3.0", "> 2.0 <= 3.0", "> 2.0 , <= 3.0", " > 2.0 , <= 3.0 ", ], ) def test_parse_constraint_multi(input): assert parse_constraint(input) == VersionRange( Version(2, 0, 0), Version(3, 0, 0), include_min=False, include_max=True ) @pytest.mark.parametrize( "input", [">=2.7,!=3.0.*,!=3.1.*", ">=2.7, !=3.0.*, !=3.1.*", ">= 2.7, != 3.0.*, != 3.1.*"], ) def test_parse_constraint_multi_wilcard(input): assert parse_constraint(input) == VersionUnion( VersionRange(Version(2, 7, 0), Version(3, 0, 0), True, False), VersionRange(Version(3, 2, 0), None, True, False), ) @pytest.mark.parametrize( "input,constraint", [ ( "!=v2.*", VersionRange(max=Version.parse("2.0")).union( VersionRange(Version.parse("3.0"), include_min=True) ), ), ( "!=2.*.*", VersionRange(max=Version.parse("2.0")).union( VersionRange(Version.parse("3.0"), include_min=True) ), ), ( "!=2.0.*", VersionRange(max=Version.parse("2.0")).union( VersionRange(Version.parse("2.1"), include_min=True) ), ), ("!=0.*", VersionRange(Version.parse("1.0"), include_min=True)), ("!=0.*.*", VersionRange(Version.parse("1.0"), include_min=True)), ], ) def test_parse_constraints_negative_wildcard(input, constraint): assert parse_constraint(input) == constraint @pytest.mark.parametrize( "input, expected", [ ("1", "1"), ("1.2", "1.2"), ("1.2.3", "1.2.3"), ("!=1", "!=1"), ("!=1.2", "!=1.2"), ("!=1.2.3", "!=1.2.3"), ("^1", ">=1,<2"), ("^1.0", ">=1.0,<2.0"), ("^1.0.0", ">=1.0.0,<2.0.0"), ("~1", ">=1,<2"), ("~1.0", ">=1.0,<1.1"), ("~1.0.0", ">=1.0.0,<1.1.0"), ], ) def test_constraints_keep_version_precision(input, expected): assert str(parse_constraint(input)) == expected @pytest.mark.parametrize( "unsorted, sorted_", [ (["1.0.3", "1.0.2", "1.0.1"], ["1.0.1", "1.0.2", "1.0.3"]), (["1.0.0.2", "1.0.0.0rc2"], ["1.0.0.0rc2", "1.0.0.2"]), (["1.0.0.0", "1.0.0.0rc2"], ["1.0.0.0rc2", "1.0.0.0"]), (["1.0.0.0.0", "1.0.0.0rc2"], ["1.0.0.0rc2", "1.0.0.0.0"]), (["1.0.0rc2", "1.0.0rc1"], ["1.0.0rc1", "1.0.0rc2"]), (["1.0.0rc2", "1.0.0b1"], ["1.0.0b1", "1.0.0rc2"]), ], ) def test_versions_are_sortable(unsorted, sorted_): unsorted = [parse_constraint(u) for u in unsorted] sorted_ = [parse_constraint(s) for s in sorted_] assert sorted(unsorted) == sorted_ requirements-detector-1.2.2/tests/test_poetry_semver/test_version.py000066400000000000000000000113551443205141000262470ustar00rootroot00000000000000import pytest from requirements_detector.poetry_semver import EmptyConstraint, Version, VersionRange from requirements_detector.poetry_semver.exceptions import ParseVersionError @pytest.mark.parametrize( "input,version", [ ("1.0.0", Version(1, 0, 0)), ("1", Version(1, 0, 0)), ("1.0", Version(1, 0, 0)), ("1b1", Version(1, 0, 0, pre="beta1")), ("1.0b1", Version(1, 0, 0, pre="beta1")), ("1.0.0b1", Version(1, 0, 0, pre="beta1")), ("1.0.0-b1", Version(1, 0, 0, pre="beta1")), ("1.0.0-beta.1", Version(1, 0, 0, pre="beta1")), ("1.0.0+1", Version(1, 0, 0, build="1")), ("1.0.0-1", Version(1, 0, 0, build="1")), ("1.0.0.0", Version(1, 0, 0)), ("1.0.0-post", Version(1, 0, 0)), ("1.0.0-post1", Version(1, 0, 0, build="1")), ("0.6c", Version(0, 6, 0, pre="rc0")), ("0.6pre", Version(0, 6, 0, pre="rc0")), ], ) def test_parse_valid(input, version): parsed = Version.parse(input) assert parsed == version assert parsed.text == input @pytest.mark.parametrize("input", [(None, "example")]) def test_parse_invalid(input): with pytest.raises(ParseVersionError): Version.parse(input) def test_comparison(): versions = [ "1.0.0-alpha", "1.0.0-alpha.1", "1.0.0-beta.2", "1.0.0-beta.11", "1.0.0-rc.1", "1.0.0-rc.1+build.1", "1.0.0", "1.0.0+0.3.7", "1.3.7+build", "1.3.7+build.2.b8f12d7", "1.3.7+build.11.e0f985a", "2.0.0", "2.1.0", "2.2.0", "2.11.0", "2.11.1", ] for i in range(len(versions)): for j in range(len(versions)): a = Version.parse(versions[i]) b = Version.parse(versions[j]) assert (a < b) == (i < j) assert (a > b) == (i > j) assert (a <= b) == (i <= j) assert (a >= b) == (i >= j) assert (a == b) == (i == j) assert (a != b) == (i != j) def test_equality(): assert Version.parse("1.2.3") == Version.parse("01.2.3") assert Version.parse("1.2.3") == Version.parse("1.02.3") assert Version.parse("1.2.3") == Version.parse("1.2.03") assert Version.parse("1.2.3-1") == Version.parse("1.2.3-01") assert Version.parse("1.2.3+1") == Version.parse("1.2.3+01") def test_allows(): v = Version.parse("1.2.3") assert v.allows(v) assert not v.allows(Version.parse("2.2.3")) assert not v.allows(Version.parse("1.3.3")) assert not v.allows(Version.parse("1.2.4")) assert not v.allows(Version.parse("1.2.3-dev")) assert not v.allows(Version.parse("1.2.3+build")) def test_allows_all(): v = Version.parse("1.2.3") assert v.allows_all(v) assert not v.allows_all(Version.parse("0.0.3")) assert not v.allows_all( VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4")) ) assert not v.allows_all(VersionRange()) assert v.allows_all(EmptyConstraint()) def test_allows_any(): v = Version.parse("1.2.3") assert v.allows_any(v) assert not v.allows_any(Version.parse("0.0.3")) assert v.allows_any(VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4"))) assert v.allows_any(VersionRange()) assert not v.allows_any(EmptyConstraint()) def test_intersect(): v = Version.parse("1.2.3") assert v.intersect(v) == v assert v.intersect(Version.parse("1.1.4")).is_empty() assert ( v.intersect(VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4"))) == v ) assert ( Version.parse("1.1.4") .intersect(VersionRange(v, Version.parse("1.2.4"))) .is_empty() ) def test_union(): v = Version.parse("1.2.3") assert v.union(v) == v result = v.union(Version.parse("0.8.0")) assert result.allows(v) assert result.allows(Version.parse("0.8.0")) assert not result.allows(Version.parse("1.1.4")) range = VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4")) assert v.union(range) == range union = Version.parse("1.1.4").union( VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4")) ) assert union == VersionRange( Version.parse("1.1.4"), Version.parse("1.2.4"), include_min=True ) result = v.union(VersionRange(Version.parse("0.0.3"), Version.parse("1.1.4"))) assert result.allows(v) assert result.allows(Version.parse("0.1.0")) def test_difference(): v = Version.parse("1.2.3") assert v.difference(v).is_empty() assert v.difference(Version.parse("0.8.0")) == v assert v.difference( VersionRange(Version.parse("1.1.4"), Version.parse("1.2.4")) ).is_empty() assert ( v.difference(VersionRange(Version.parse("1.4.0"), Version.parse("3.0.0"))) == v ) requirements-detector-1.2.2/tests/test_poetry_semver/test_version_range.py000066400000000000000000000177061443205141000274310ustar00rootroot00000000000000import pytest from requirements_detector.poetry_semver import EmptyConstraint, Version, VersionRange @pytest.fixture() def v003(): return Version.parse("0.0.3") @pytest.fixture() def v010(): return Version.parse("0.1.0") @pytest.fixture() def v080(): return Version.parse("0.8.0") @pytest.fixture() def v072(): return Version.parse("0.7.2") @pytest.fixture() def v114(): return Version.parse("1.1.4") @pytest.fixture() def v123(): return Version.parse("1.2.3") @pytest.fixture() def v124(): return Version.parse("1.2.4") @pytest.fixture() def v130(): return Version.parse("1.3.0") @pytest.fixture() def v140(): return Version.parse("1.4.0") @pytest.fixture() def v200(): return Version.parse("2.0.0") @pytest.fixture() def v234(): return Version.parse("2.3.4") @pytest.fixture() def v250(): return Version.parse("2.5.0") @pytest.fixture() def v300(): return Version.parse("3.0.0") def test_allows_all(v003, v010, v080, v114, v123, v124, v140, v200, v234, v250, v300): assert VersionRange(v123, v250).allows_all(EmptyConstraint()) range = VersionRange(v123, v250, include_max=True) assert not range.allows_all(v123) assert range.allows_all(v124) assert range.allows_all(v250) assert not range.allows_all(v300) # with no min range = VersionRange(max=v250) assert range.allows_all(VersionRange(v080, v140)) assert not range.allows_all(VersionRange(v080, v300)) assert range.allows_all(VersionRange(max=v140)) assert not range.allows_all(VersionRange(max=v300)) assert range.allows_all(range) assert not range.allows_all(VersionRange()) # with no max range = VersionRange(min=v010) assert range.allows_all(VersionRange(v080, v140)) assert not range.allows_all(VersionRange(v003, v140)) assert range.allows_all(VersionRange(v080)) assert not range.allows_all(VersionRange(v003)) assert range.allows_all(range) assert not range.allows_all(VersionRange()) # Allows bordering range that is not more inclusive exclusive = VersionRange(v010, v250) inclusive = VersionRange(v010, v250, True, True) assert inclusive.allows_all(exclusive) assert inclusive.allows_all(inclusive) assert not exclusive.allows_all(inclusive) assert exclusive.allows_all(exclusive) # Allows unions that are completely contained range = VersionRange(v114, v200) assert range.allows_all(VersionRange(v123, v124).union(v140)) assert not range.allows_all(VersionRange(v010, v124).union(v140)) assert not range.allows_all(VersionRange(v123, v234).union(v140)) def test_allows_any( v003, v010, v072, v080, v114, v123, v124, v140, v200, v234, v250, v300 ): # disallows an empty constraint assert not VersionRange(v123, v250).allows_any(EmptyConstraint()) # allows allowed versions range = VersionRange(v123, v250, include_max=True) assert not range.allows_any(v123) assert range.allows_any(v124) assert range.allows_any(v250) assert not range.allows_any(v300) # with no min range = VersionRange(max=v200) assert range.allows_any(VersionRange(v140, v300)) assert not range.allows_any(VersionRange(v234, v300)) assert range.allows_any(VersionRange(v140)) assert not range.allows_any(VersionRange(v234)) assert range.allows_any(range) # with no max range = VersionRange(min=v072) assert range.allows_any(VersionRange(v003, v140)) assert not range.allows_any(VersionRange(v003, v010)) assert range.allows_any(VersionRange(max=v080)) assert not range.allows_any(VersionRange(max=v003)) assert range.allows_any(range) # with min and max range = VersionRange(v072, v200) assert range.allows_any(VersionRange(v003, v140)) assert range.allows_any(VersionRange(v140, v300)) assert not range.allows_any(VersionRange(v003, v010)) assert not range.allows_any(VersionRange(v234, v300)) assert not range.allows_any(VersionRange(max=v010)) assert not range.allows_any(VersionRange(v234)) assert range.allows_any(range) # allows a bordering range when both are inclusive assert not VersionRange(max=v250).allows_any(VersionRange(min=v250)) assert not VersionRange(max=v250, include_max=True).allows_any( VersionRange(min=v250) ) assert not VersionRange(max=v250).allows_any( VersionRange(min=v250, include_min=True) ) assert not VersionRange(min=v250).allows_any(VersionRange(max=v250)) assert VersionRange(max=v250, include_max=True).allows_any( VersionRange(min=v250, include_min=True) ) # allows unions that are partially contained' range = VersionRange(v114, v200) assert range.allows_any(VersionRange(v010, v080).union(v140)) assert range.allows_any(VersionRange(v123, v234).union(v300)) assert not range.allows_any(VersionRange(v234, v300).union(v010)) # pre-release min does not allow lesser than itself range = VersionRange(Version.parse("1.9b1"), include_min=True) assert not range.allows_any( VersionRange( Version.parse("1.8.0"), Version.parse("1.9.0"), include_min=True, always_include_max_prerelease=True, ) ) def test_intersect(v114, v123, v124, v200, v250, v300): # two overlapping ranges assert VersionRange(v123, v250).intersect(VersionRange(v200, v300)) == VersionRange( v200, v250 ) # a non-overlapping range allows no versions a = VersionRange(v114, v124) b = VersionRange(v200, v250) assert a.intersect(b).is_empty() # adjacent ranges allow no versions if exclusive a = VersionRange(v114, v124) b = VersionRange(v124, v200) assert a.intersect(b).is_empty() # adjacent ranges allow version if inclusive a = VersionRange(v114, v124, include_max=True) b = VersionRange(v124, v200, include_min=True) assert a.intersect(b) == v124 # with an open range open = VersionRange() a = VersionRange(v114, v124) assert open.intersect(open) == open assert open.intersect(a) == a # returns the version if the range allows it assert VersionRange(v114, v124).intersect(v123) == v123 assert VersionRange(v123, v124).intersect(v114).is_empty() def test_union( v003, v010, v072, v080, v114, v123, v124, v130, v140, v200, v234, v250, v300 ): # with a version returns the range if it contains the version range = VersionRange(v114, v124) assert range.union(v123) == range # with a version on the edge of the range, expands the range range = VersionRange(v114, v124) assert range.union(v124) == VersionRange(v114, v124, include_max=True) assert range.union(v114) == VersionRange(v114, v124, include_min=True) # with a version allows both the range and the version if the range # doesn't contain the version result = VersionRange(v003, v114).union(v124) assert result.allows(v010) assert not result.allows(v123) assert result.allows(v124) # returns a VersionUnion for a disjoint range result = VersionRange(v003, v114).union(VersionRange(v130, v200)) assert result.allows(v080) assert not result.allows(v123) assert result.allows(v140) # considers open ranges disjoint result = VersionRange(v003, v114).union(VersionRange(v114, v200)) assert result.allows(v080) assert not result.allows(v114) assert result.allows(v140) result = VersionRange(v114, v200).union(VersionRange(v003, v114)) assert result.allows(v080) assert not result.allows(v114) assert result.allows(v140) # returns a merged range for an overlapping range result = VersionRange(v003, v114).union(VersionRange(v080, v200)) assert result == VersionRange(v003, v200) # considers closed ranges overlapping result = VersionRange(v003, v114, include_max=True).union(VersionRange(v114, v200)) assert result == VersionRange(v003, v200) result = VersionRange(v003, v114).union(VersionRange(v114, v200, include_min=True)) assert result == VersionRange(v003, v200) requirements-detector-1.2.2/tox.ini000066400000000000000000000002511443205141000173510ustar00rootroot00000000000000[tox] envlist = py37,py38,py39,py310,py311 isolated_build = true skip_missing_interpreters = true [testenv] deps=pytest commands= pytest -s detect-requirements