├── src └── poetryup │ ├── __init__.py │ ├── main.py │ └── pyproject.py ├── media └── poetryup_demo.gif ├── .github ├── workflows │ ├── release-drafter.yml │ ├── update-dependencies.yaml │ ├── test.yaml │ └── publish.yaml └── release-drafter.yml ├── .pre-commit-config.yaml ├── tests ├── unit │ ├── fixtures │ │ ├── input_pyproject │ │ │ ├── pyproject.toml │ │ │ ├── pyproject_with_dependency_containing_capital_letters.toml │ │ │ ├── pyproject_with_restricted_dependency.toml │ │ │ ├── pyproject_with_git_dependency.toml │ │ │ └── pyproject_with_dependency_groups.toml │ │ └── expected_pyproject │ │ │ ├── pyproject.toml │ │ │ ├── pyproject_with_dependency_containing_capital_letters.toml │ │ │ ├── pyproject_with_restricted_dependency.toml │ │ │ ├── pyproject_with_git_dependency.toml │ │ │ └── pyproject_with_dependency_groups.toml │ └── test_pyproject.py └── conftest.py ├── LICENSE ├── pyproject.toml ├── README.md ├── .gitignore └── poetry.lock /src/poetryup/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /media/poetryup_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-sandall/poetryup/master/media/poetryup_demo.gif -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | # This workflow drafts release notes as pull requests are merged into master 2 | 3 | name: Release Drafter 4 | 5 | on: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | update_release_draft: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: release-drafter/release-drafter@v5 15 | env: 16 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v4.0.1 4 | hooks: 5 | - id: trailing-whitespace 6 | - id: end-of-file-fixer 7 | - id: check-json 8 | - id: check-yaml 9 | - id: check-toml 10 | 11 | - repo: local 12 | hooks: 13 | - id: flake8 14 | name: flake8 15 | entry: flake8 16 | language: system 17 | types: [python] 18 | - id: black-check 19 | name: black-check 20 | entry: black --check 21 | language: system 22 | types: [python] 23 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: '🚀 Features' 5 | labels: 6 | - 'feature' 7 | - 'enhancement' 8 | - title: '🐛 Bug Fixes' 9 | labels: 10 | - 'fix' 11 | - 'bugfix' 12 | - 'bug' 13 | - title: '🧰 Maintenance' 14 | label: 'chore' 15 | change-template: '- $TITLE @$AUTHOR (#$NUMBER)' 16 | change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. 17 | version-resolver: 18 | major: 19 | labels: 20 | - 'major' 21 | minor: 22 | labels: 23 | - 'minor' 24 | patch: 25 | labels: 26 | - 'patch' 27 | default: patch 28 | template: | 29 | ## Changes 30 | 31 | $CHANGES 32 | -------------------------------------------------------------------------------- /tests/unit/fixtures/input_pyproject/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = "^0.1.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/expected_pyproject/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = "^0.2.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/expected_pyproject/pyproject_with_dependency_containing_capital_letters.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | PoetryUp = "^0.2.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/input_pyproject/pyproject_with_dependency_containing_capital_letters.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | PoetryUp = "^0.1.0" 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/input_pyproject/pyproject_with_restricted_dependency.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = {version = "^0.1.0", python = "<3.7"} 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/expected_pyproject/pyproject_with_restricted_dependency.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = {version = "^0.2.0", python = "<3.7"} 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/input_pyproject/pyproject_with_git_dependency.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = { git = "https://github.com/MousaZeidBaker/poetryup.git" } 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/expected_pyproject/pyproject_with_git_dependency.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | poetryup = { git = "https://github.com/MousaZeidBaker/poetryup.git" } 24 | 25 | [tool.poetry.dev-dependencies] 26 | 27 | [tool.poetry.scripts] 28 | poetryup = "src.test_poetryup.test_poetryup:main" 29 | 30 | [build-system] 31 | requires = ["poetry-core>=1.0.0"] 32 | build-backend = "poetry.core.masonry.api" 33 | -------------------------------------------------------------------------------- /tests/unit/fixtures/expected_pyproject/pyproject_with_dependency_groups.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | 24 | [tool.poetry.group.main.dependencies] 25 | poetryup = "^0.2.0" 26 | 27 | [tool.poetry.group.dev.dependencies] 28 | 29 | [tool.poetry.scripts] 30 | poetryup = "src.test_poetryup.test_poetryup:main" 31 | 32 | [build-system] 33 | requires = ["poetry-core>=1.0.0"] 34 | build-backend = "poetry.core.masonry.api" 35 | -------------------------------------------------------------------------------- /tests/unit/fixtures/input_pyproject/pyproject_with_dependency_groups.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "test-poetryup" 3 | version = "0.1.0" 4 | description = "Test PoetryUp" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "test_poetryup", from = "src" } 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | include = ["LICENSE"] 20 | 21 | [tool.poetry.dependencies] 22 | python = "^3.6" 23 | 24 | [tool.poetry.group.main.dependencies] 25 | poetryup = "^0.1.0" 26 | 27 | [tool.poetry.group.dev.dependencies] 28 | 29 | [tool.poetry.scripts] 30 | poetryup = "src.test_poetryup.test_poetryup:main" 31 | 32 | [build-system] 33 | requires = ["poetry-core>=1.0.0"] 34 | build-backend = "poetry.core.masonry.api" 35 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from pytest_mock import MockerFixture 3 | 4 | from poetryup.pyproject import Pyproject 5 | 6 | 7 | @pytest.fixture(scope="function") 8 | def mock_poetry_commands(mocker: MockerFixture) -> None: 9 | """Mock poetry commands""" 10 | 11 | mocker.patch.object( 12 | Pyproject, 13 | "_Pyproject__get_poetry_version", 14 | return_value="1.1.0", 15 | ) 16 | 17 | mocker.patch.object( 18 | Pyproject, 19 | "_Pyproject__run_poetry_show", 20 | return_value=( 21 | "poetryup 0.2.0 Update dependencies and bump their version in the " 22 | "pyproject.toml file" 23 | "\n└── toml >=0.10.2,<0.11.0" 24 | ), 25 | ) 26 | 27 | mocker.patch.object( 28 | Pyproject, 29 | "_Pyproject__run_poetry_update", 30 | return_value=None, 31 | ) 32 | 33 | mocker.patch.object( 34 | Pyproject, 35 | "_Pyproject__run_poetry_add", 36 | return_value=None, 37 | ) 38 | -------------------------------------------------------------------------------- /src/poetryup/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import logging 4 | import os 5 | from pathlib import Path 6 | 7 | import typer 8 | 9 | from poetryup.pyproject import Pyproject 10 | 11 | logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO").upper()) 12 | 13 | app = typer.Typer(add_completion=False) 14 | 15 | 16 | @app.command() 17 | def poetryup( 18 | latest: bool = typer.Option( 19 | default=False, 20 | help="Whether to update dependencies to their latest version.", 21 | ), 22 | ): 23 | """Update dependencies and bump their version in pyproject.toml file""" 24 | 25 | try: 26 | pyproject_str = Path("pyproject.toml").read_text() 27 | except FileNotFoundError: 28 | raise Exception( 29 | "poetryup couldn't find a pyproject.toml file in current directory" 30 | ) 31 | 32 | pyproject = Pyproject(pyproject_str) 33 | pyproject.update_dependencies(latest) 34 | Path("pyproject.toml").write_text(pyproject.dumps()) 35 | 36 | 37 | if __name__ == "__main__": 38 | app() 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Mousa Zeid Baker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/update-dependencies.yaml: -------------------------------------------------------------------------------- 1 | name: Update Dependencies 2 | 3 | # controls when the action will run 4 | on: 5 | schedule: 6 | - cron: '0 6 1 * *' # at 06:00 on the first day of every month 7 | workflow_dispatch: # enables manual trigger 8 | 9 | jobs: 10 | update_dependencies: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout repository 14 | uses: actions/checkout@v2 15 | 16 | - name: Set up python 3.8 17 | uses: actions/setup-python@v2 18 | with: 19 | python-version: 3.8 20 | 21 | - name: Install poetry 1.1.12 22 | uses: snok/install-poetry@v1 23 | with: 24 | version: 1.1.12 25 | 26 | - name: Update dependencies 27 | run: | 28 | pip install poetryup 29 | poetryup 30 | if [ -n "$(git status --porcelain)" ]; then 31 | # working tree NOT clean, bump project version 32 | poetry version patch 33 | fi 34 | 35 | - name: Create Pull Request 36 | uses: peter-evans/create-pull-request@v3 37 | with: 38 | token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} 39 | commit-message: Update dependencies 40 | title: Update dependencies 41 | labels: | 42 | patch 43 | branch: update-dependencies 44 | delete-branch: true 45 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "poetryup" 3 | version = "0.5.1" 4 | description = "Update dependencies and bump their version in the pyproject.toml file" 5 | authors = ["Mousa Zeid Baker"] 6 | packages = [ 7 | { include = "poetryup", from = "src" }, 8 | ] 9 | license = "MIT" 10 | readme = "README.md" 11 | homepage = "https://github.com/MousaZeidBaker/poetryup" 12 | repository = "https://github.com/MousaZeidBaker/poetryup" 13 | keywords=[ 14 | "packaging", 15 | "dependency", 16 | "poetry", 17 | "poetryup", 18 | ] 19 | classifiers=[ 20 | "Development Status :: 5 - Production/Stable", 21 | "Programming Language :: Python :: 3", 22 | "License :: OSI Approved :: MIT License", 23 | "Operating System :: OS Independent", 24 | ] 25 | include = ["LICENSE"] 26 | 27 | [tool.poetry.dependencies] 28 | python = "^3.6" 29 | tomlkit = "^0.8.0" 30 | typer = "^0.4.0" 31 | packaging = "^21.3" 32 | 33 | [tool.poetry.dev-dependencies] 34 | pytest = "^6.2.5" 35 | pytest-mock = "^3.6.1" 36 | black = [ 37 | {version = "^20.8b1", python = "<=3.6.2"}, 38 | {version = "^21.12b0", python = ">3.6.2"}, 39 | ] 40 | flake8 = "^4.0.1" 41 | flake8-black = "^0.2.3" 42 | flake8-isort = "^4.1.1" 43 | isort = { version = "^5.10.1", python = "^3.6.1" } 44 | pre-commit = { version = "^2.17.0", python = "^3.6.1" } 45 | 46 | [tool.poetry.scripts] 47 | poetryup = "poetryup.main:app" 48 | 49 | [build-system] 50 | requires = ["poetry-core>=1.0.0"] 51 | build-backend = "poetry.core.masonry.api" 52 | 53 | [tool.black] 54 | line-length = 80 55 | 56 | [tool.isort] 57 | profile = "black" 58 | -------------------------------------------------------------------------------- /tests/unit/test_pyproject.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | 4 | from poetryup.pyproject import Pyproject 5 | 6 | 7 | def pytest_generate_tests(metafunc) -> None: 8 | input_pyproject_path = os.path.join( 9 | os.path.dirname(__file__), 10 | "fixtures/input_pyproject", 11 | ) 12 | input_pyprojects = [ 13 | file.read_text() for file in Path(input_pyproject_path).glob("*.toml") 14 | ] 15 | 16 | expected_pyproject_path = os.path.join( 17 | os.path.dirname(__file__), 18 | "fixtures/expected_pyproject", 19 | ) 20 | expected_pyprojects = [ 21 | file.read_text() 22 | for file in Path(expected_pyproject_path).glob("*.toml") 23 | ] 24 | 25 | argvalues = list(zip(input_pyprojects, expected_pyprojects)) 26 | ids = [file.name for file in Path(input_pyproject_path).glob("*.toml")] 27 | 28 | metafunc.parametrize( 29 | argnames=("input_pyproject", "expected_pyproject"), 30 | argvalues=argvalues, 31 | ids=ids, 32 | ) 33 | 34 | 35 | def test_update_dependencies( 36 | input_pyproject: str, 37 | expected_pyproject: str, 38 | mock_poetry_commands, 39 | ) -> None: 40 | pyproject = Pyproject(input_pyproject) 41 | pyproject.update_dependencies() 42 | assert pyproject.dumps() == expected_pyproject 43 | 44 | 45 | def test_update_dependencies_latest( 46 | input_pyproject: str, 47 | expected_pyproject: str, 48 | mock_poetry_commands, 49 | ) -> None: 50 | pyproject = Pyproject(input_pyproject) 51 | pyproject.update_dependencies(latest=True) 52 | assert pyproject.dumps() == expected_pyproject 53 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | # This workflow tests our package 2 | 3 | name: Test 4 | 5 | # controls when the action will run 6 | on: 7 | pull_request: 8 | branches: 9 | - '*' 10 | 11 | jobs: 12 | test: 13 | runs-on: ubuntu-latest 14 | environment: testpypi 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | python-version: [ '3.6', '3.7', '3.8', '3.9' ] 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v2 22 | 23 | - name: Set up python 3.8 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: 3.8 27 | 28 | - name: Install poetry 1.1.7 29 | uses: snok/install-poetry@v1 30 | with: 31 | version: 1.1.7 32 | 33 | - name: Install dependencies 34 | run: poetry install 35 | 36 | - name: Lint 37 | run: | 38 | source $(poetry env info --path)/bin/activate # activate virtual environment 39 | flake8 . 40 | 41 | - name: Test 42 | run: | 43 | source $(poetry env info --path)/bin/activate # activate virtual environment 44 | pytest tests 45 | poetryup 46 | 47 | - name: Validate Project Version 48 | run: | 49 | source $(poetry env info --path)/bin/activate # activate virtual environment 50 | NEW_VERSION=$(poetry version --short) 51 | git fetch --no-tags && git checkout --force master 52 | OLD_VERSION=$(poetry version --short) 53 | echo "NEW_VERSION ${NEW_VERSION} OLD_VERSION ${OLD_VERSION}" 54 | if [[ "$NEW_VERSION" == "$OLD_VERSION" ]]; then 55 | echo "Make sure to bump project version" 56 | exit 1 # operation not permitted 57 | fi 58 | 59 | - name: Build 60 | run: poetry build 61 | 62 | - name: Publish testpypi dry-run 63 | env: 64 | POETRY_REPOSITORIES_TESTPYPI_URL: https://test.pypi.org/legacy/ 65 | POETRY_HTTP_BASIC_TESTPYPI_USERNAME: __token__ 66 | POETRY_HTTP_BASIC_TESTPYPI_PASSWORD: ${{secrets.TESTPYPI_API_TOKEN}} 67 | run: poetry publish -r testpypi --dry-run 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PoetryUp 2 | 3 | ![build](https://github.com/MousaZeidBaker/poetryup/workflows/Publish/badge.svg) 4 | ![test](https://github.com/MousaZeidBaker/poetryup/workflows/Test/badge.svg) 5 | [![License](https://img.shields.io/badge/License-MIT-yellow)](LICENSE) 6 | ![python_version](https://img.shields.io/badge/python-%3E=3.6-blue) 7 | [![pypi_v](https://img.shields.io/pypi/v/poetryup)](https://pypi.org/project/poetryup) 8 | [![pypi_dm](https://img.shields.io/pypi/dm/poetryup)](https://pypi.org/project/poetryup) 9 | 10 | PoetryUp updates dependencies and bumps their version in the `pyproject.toml` 11 | file with respect to their version constraint. The `poetry.lock` file will be 12 | recreated as well. PoetryUp runs 13 | [poetry](https://github.com/python-poetry/poetry) commands, thus it's required 14 | to be installed. The difference between running `poetry update` and `poetryup`, 15 | is that the latter also modifies the `pyproject.toml` file. 16 | 17 | ![poetryup_demo](https://raw.githubusercontent.com/MousaZeidBaker/poetryup/master/media/poetryup_demo.gif) 18 | 19 | ## Usage 20 | ```shell 21 | poetryup --help 22 | ``` 23 | 24 | ## Automate Dependency Updates with GitHub Actions 25 | Use PoetryUp with GitHub actions to automate the process of updating 26 | dependencies, for reference see this project's [workflow 27 | configuration](https://github.com/MousaZeidBaker/poetryup/blob/master/.github/workflows/update-dependencies.yaml). 28 | 29 | ## Contributing 30 | Contributions are welcome via pull requests. 31 | 32 | ## Issues 33 | If you encounter any problems, please file an 34 | [issue](https://github.com/MousaZeidBaker/poetryup/issues) along with a detailed 35 | description. 36 | 37 | ## Develop 38 | Activate virtual environment 39 | ```shell 40 | poetry shell 41 | ``` 42 | 43 | Install dependencies 44 | ```shell 45 | poetry install --remove-untracked 46 | ``` 47 | 48 | Install git hooks 49 | ```shell 50 | pre-commit install --hook-type pre-commit 51 | ``` 52 | 53 | Run tests 54 | ```shell 55 | pytest tests 56 | ``` 57 | 58 | Run linter 59 | ```shell 60 | flake8 . 61 | ``` 62 | 63 | Format code 64 | ```shell 65 | black . 66 | ``` 67 | 68 | Sort imports 69 | ```shell 70 | isort . 71 | ``` 72 | 73 | Install current project from branch 74 | ```shell 75 | poetry add git+https://github.com/MousaZeidBaker/poetryup.git#branch-name 76 | ``` 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | 131 | # IntelliJ’s project specific settings files 132 | .idea 133 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | # This workflow builds and publishes package distribution to PyPI 2 | 3 | name: Publish 4 | 5 | # controls when the action will run 6 | on: 7 | push: 8 | branches: 9 | - 'master' 10 | 11 | jobs: 12 | publish_testpypi: 13 | runs-on: ubuntu-latest 14 | environment: testpypi 15 | steps: 16 | - name: Checkout repository 17 | uses: actions/checkout@v2 18 | 19 | - name: Set up python 3.8 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: 3.8 23 | 24 | - name: Install poetry 1.1.7 25 | uses: snok/install-poetry@v1 26 | with: 27 | version: 1.1.7 28 | 29 | - name: Install dependencies 30 | run: poetry install 31 | 32 | - name: Lint 33 | run: | 34 | source $(poetry env info --path)/bin/activate # activate virtual environment 35 | flake8 . 36 | 37 | - name: Test 38 | run: | 39 | source $(poetry env info --path)/bin/activate # activate virtual environment 40 | pytest tests 41 | 42 | - name: Build 43 | run: poetry build 44 | 45 | - name: Publish testpypi 46 | env: 47 | POETRY_REPOSITORIES_TESTPYPI_URL: https://test.pypi.org/legacy/ 48 | POETRY_HTTP_BASIC_TESTPYPI_USERNAME: __token__ 49 | POETRY_HTTP_BASIC_TESTPYPI_PASSWORD: ${{secrets.TESTPYPI_API_TOKEN}} 50 | run: poetry publish -r testpypi 51 | 52 | publish_pypi: 53 | runs-on: ubuntu-latest 54 | environment: pypi 55 | steps: 56 | - name: Checkout repository 57 | uses: actions/checkout@v2 58 | 59 | - name: Set up python 3.8 60 | uses: actions/setup-python@v2 61 | with: 62 | python-version: 3.8 63 | 64 | - name: Install poetry 1.1.7 65 | uses: snok/install-poetry@v1 66 | with: 67 | version: 1.1.7 68 | 69 | - name: Install dependencies 70 | run: poetry install 71 | 72 | - name: Lint 73 | run: | 74 | source $(poetry env info --path)/bin/activate # activate virtual environment 75 | flake8 . 76 | 77 | - name: Test 78 | run: | 79 | source $(poetry env info --path)/bin/activate # activate virtual environment 80 | pytest tests 81 | 82 | - name: Build 83 | run: poetry build 84 | 85 | - name: Publish pypi 86 | env: 87 | POETRY_HTTP_BASIC_PYPI_USERNAME: __token__ 88 | POETRY_HTTP_BASIC_PYPI_PASSWORD: ${{secrets.PYPI_API_TOKEN}} 89 | run: poetry publish 90 | -------------------------------------------------------------------------------- /src/poetryup/pyproject.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import re 3 | import subprocess 4 | from dataclasses import dataclass 5 | from typing import List, Optional, Union 6 | 7 | import tomlkit 8 | from packaging import version as version_ 9 | from tomlkit import items 10 | 11 | 12 | @dataclass 13 | class Dependency: 14 | """A class to represent a dependency""" 15 | 16 | name: str 17 | version: Union[items.String, items.InlineTable, items.Array] 18 | group: str 19 | 20 | 21 | class Pyproject: 22 | """A class to represent a pyproject.toml configuration file. 23 | 24 | The pyproject.toml was defined in PEP 518 and expanded in PEP 621. 25 | https://www.python.org/dev/peps/pep-0518/ 26 | https://www.python.org/dev/peps/pep-0621/ 27 | 28 | Args: 29 | pyproject_str: The pyproject.toml file parsed as a string 30 | """ 31 | 32 | def __init__(self, pyproject_str: str) -> None: 33 | self.pyproject = tomlkit.loads(pyproject_str) 34 | self.poetry_version = version_.parse(self.__get_poetry_version()) 35 | 36 | def dumps(self) -> str: 37 | """Dumps pyproject into a string.""" 38 | 39 | return tomlkit.dumps(self.pyproject) 40 | 41 | def list_dependencies(self) -> List[Dependency]: 42 | """Returns pyproject dependencies""" 43 | 44 | dependencies: List[Dependency] = [] 45 | table = self.pyproject["tool"]["poetry"] 46 | 47 | # get default dependencies 48 | for name, version in table.get("dependencies", {}).items(): 49 | if name == "python": 50 | # ignore python dependency 51 | continue 52 | dependency = Dependency( 53 | name=name, 54 | version=version, 55 | group="default", 56 | ) 57 | dependencies.append(dependency) 58 | 59 | # get dev-dependencies 60 | for name, version in table.get("dev-dependencies", {}).items(): 61 | dependency = Dependency( 62 | name=name, 63 | version=version, 64 | group="dev", 65 | ) 66 | dependencies.append(dependency) 67 | 68 | # get dependencies organized in groups 69 | for group, deps in table.get("group", {}).items(): 70 | for name, version in deps["dependencies"].items(): 71 | dependency = Dependency( 72 | name=name, 73 | version=version, 74 | group=group, 75 | ) 76 | dependencies.append(dependency) 77 | 78 | return dependencies 79 | 80 | def list_lock_dependencies(self) -> List[Dependency]: 81 | """Returns pyproject dependencies with their lock version""" 82 | 83 | # create list of lock dependencies 84 | output = self.__run_poetry_show() 85 | pattern = re.compile("^[a-zA-Z-]+") 86 | lock_deps: List[Dependency] = [] 87 | for line in output.split("\n"): 88 | if pattern.match(line) is not None: 89 | name, version, *_ = line.split() 90 | dependency = Dependency( 91 | name=name, 92 | version=version, 93 | group="", 94 | ) 95 | lock_deps.append(dependency) 96 | 97 | # list dependencies from pyproject and set version to lock version 98 | dependencies = self.list_dependencies() 99 | for dependency in dependencies: 100 | # find corresponding lock dependency 101 | name = dependency.name.lower() 102 | lock_dep = next(dep for dep in lock_deps if dep.name == name) 103 | 104 | constraint = "" 105 | if type(dependency.version) is items.String: 106 | if dependency.version[0].startswith(("^", "~")): 107 | constraint = dependency.version[0] 108 | dependency.version = constraint + lock_dep.version 109 | elif ( 110 | type(dependency.version) is items.InlineTable 111 | and dependency.version.get("version") is not None 112 | ): 113 | if dependency.version["version"].startswith(("^", "~")): 114 | constraint = dependency.version["version"][0] 115 | dependency.version["version"] = constraint + lock_dep.version 116 | 117 | return dependencies 118 | 119 | def update_dependencies(self, latest: bool = False) -> None: 120 | """Update dependencies and bump their version in pyproject 121 | 122 | Args: 123 | latest: Whether to update dependencies to their latest version 124 | """ 125 | 126 | if latest: 127 | logging.info("Updating dependencies to their latest version") 128 | # sort dependencies into their groups and add them at once in order 129 | # to avoid version solver error in case dependencies depend on each 130 | # other 131 | groups = {} 132 | for dependency in self.list_dependencies(): 133 | if type(dependency.version) is items.String: 134 | groups[dependency.group] = groups.get( 135 | dependency.group, [] 136 | ) + [f"{dependency.name}@latest"] 137 | 138 | for group, packages in groups.items(): 139 | self.__run_poetry_add( 140 | packages=packages, 141 | group=group, 142 | ) 143 | else: 144 | logging.info("Running poetry update command") 145 | self.__run_poetry_update() 146 | 147 | # bump versions in pyproject 148 | table = self.pyproject["tool"]["poetry"] 149 | for dependency in self.list_lock_dependencies(): 150 | if dependency.group == "default": 151 | table["dependencies"][dependency.name] = dependency.version 152 | elif ( 153 | dependency.group == "dev" 154 | and table.get("dev-dependencies", {}).get(dependency.name) 155 | is not None 156 | ): 157 | table["dev-dependencies"][dependency.name] = dependency.version 158 | elif ( 159 | table.get("group", {}) 160 | .get(dependency.group, {}) 161 | .get("dependencies", {}) 162 | .get(dependency.name) 163 | is not None 164 | ): 165 | table["group"][dependency.group]["dependencies"][ 166 | dependency.name 167 | ] = dependency.version 168 | else: 169 | logging.info(f"Couldn't bump dependency '{dependency.name}'") 170 | 171 | @staticmethod 172 | def __get_poetry_version() -> str: 173 | """Return the installed poetry version 174 | 175 | Returns: 176 | The poetry version installed 177 | """ 178 | 179 | return ( 180 | subprocess.run( 181 | ["poetry", "--version"], 182 | capture_output=True, 183 | ) 184 | .stdout.decode() # command returns: 'Poetry version x.y.z' 185 | .rsplit(" ", 1) 186 | .pop() 187 | .strip() 188 | ) 189 | 190 | @staticmethod 191 | def __run_poetry_show() -> str: 192 | """Run poetry show command 193 | 194 | Returns: 195 | The output from the poetry show command 196 | """ 197 | 198 | return subprocess.run( 199 | ["poetry", "show", "--tree"], 200 | capture_output=True, 201 | ).stdout.decode() 202 | 203 | @staticmethod 204 | def __run_poetry_update() -> None: 205 | """Run poetry update command""" 206 | 207 | subprocess.run(["poetry", "update"]) 208 | 209 | def __run_poetry_add( 210 | self, 211 | packages: List[str], 212 | group: Optional[str], 213 | ) -> None: 214 | """Run poetry add command 215 | 216 | Args: 217 | package: The package(s) to add 218 | group: The group the package(s) should be added to 219 | """ 220 | 221 | if group is None or group == "default": 222 | subprocess.run(["poetry", "add", *packages]) 223 | elif group == "dev" and self.poetry_version < version_.parse("1.2.0"): 224 | subprocess.run(["poetry", "add", *packages, f"--{group}"]) 225 | elif self.poetry_version >= version_.parse("1.2.0"): 226 | subprocess.run(["poetry", "add", *packages, f"--group {group}"]) 227 | else: 228 | logging.info(f"Couldn't add package(s) '{packages}'") 229 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "appdirs" 3 | version = "1.4.4" 4 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 5 | category = "dev" 6 | optional = false 7 | python-versions = "*" 8 | 9 | [[package]] 10 | name = "atomicwrites" 11 | version = "1.4.0" 12 | description = "Atomic file writes." 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 16 | 17 | [[package]] 18 | name = "attrs" 19 | version = "21.4.0" 20 | description = "Classes Without Boilerplate" 21 | category = "dev" 22 | optional = false 23 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 24 | 25 | [package.extras] 26 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 27 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 28 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 29 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 30 | 31 | [[package]] 32 | name = "black" 33 | version = "20.8b1" 34 | description = "The uncompromising code formatter." 35 | category = "dev" 36 | optional = false 37 | python-versions = ">=3.6" 38 | 39 | [package.dependencies] 40 | appdirs = "*" 41 | click = ">=7.1.2" 42 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} 43 | mypy-extensions = ">=0.4.3" 44 | pathspec = ">=0.6,<1" 45 | regex = ">=2020.1.8" 46 | toml = ">=0.10.1" 47 | typed-ast = ">=1.4.0" 48 | typing-extensions = ">=3.7.4" 49 | 50 | [package.extras] 51 | colorama = ["colorama (>=0.4.3)"] 52 | d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] 53 | 54 | [[package]] 55 | name = "black" 56 | version = "21.12b0" 57 | description = "The uncompromising code formatter." 58 | category = "dev" 59 | optional = false 60 | python-versions = ">=3.6.2" 61 | 62 | [package.dependencies] 63 | click = ">=7.1.2" 64 | dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} 65 | mypy-extensions = ">=0.4.3" 66 | pathspec = ">=0.9.0,<1" 67 | platformdirs = ">=2" 68 | tomli = ">=0.2.6,<2.0.0" 69 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} 70 | typing-extensions = [ 71 | {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, 72 | {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, 73 | ] 74 | 75 | [package.extras] 76 | colorama = ["colorama (>=0.4.3)"] 77 | d = ["aiohttp (>=3.7.4)"] 78 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 79 | python2 = ["typed-ast (>=1.4.3)"] 80 | uvloop = ["uvloop (>=0.15.2)"] 81 | 82 | [[package]] 83 | name = "cfgv" 84 | version = "3.3.1" 85 | description = "Validate configuration and produce human readable error messages." 86 | category = "dev" 87 | optional = false 88 | python-versions = ">=3.6.1" 89 | 90 | [[package]] 91 | name = "click" 92 | version = "8.0.3" 93 | description = "Composable command line interface toolkit" 94 | category = "main" 95 | optional = false 96 | python-versions = ">=3.6" 97 | 98 | [package.dependencies] 99 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 100 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 101 | 102 | [[package]] 103 | name = "colorama" 104 | version = "0.4.4" 105 | description = "Cross-platform colored terminal text." 106 | category = "main" 107 | optional = false 108 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 109 | 110 | [[package]] 111 | name = "dataclasses" 112 | version = "0.8" 113 | description = "A backport of the dataclasses module for Python 3.6" 114 | category = "dev" 115 | optional = false 116 | python-versions = ">=3.6, <3.7" 117 | 118 | [[package]] 119 | name = "distlib" 120 | version = "0.3.4" 121 | description = "Distribution utilities" 122 | category = "dev" 123 | optional = false 124 | python-versions = "*" 125 | 126 | [[package]] 127 | name = "filelock" 128 | version = "3.4.1" 129 | description = "A platform independent file lock." 130 | category = "dev" 131 | optional = false 132 | python-versions = ">=3.6" 133 | 134 | [package.extras] 135 | docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] 136 | testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] 137 | 138 | [[package]] 139 | name = "flake8" 140 | version = "4.0.1" 141 | description = "the modular source code checker: pep8 pyflakes and co" 142 | category = "dev" 143 | optional = false 144 | python-versions = ">=3.6" 145 | 146 | [package.dependencies] 147 | importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} 148 | mccabe = ">=0.6.0,<0.7.0" 149 | pycodestyle = ">=2.8.0,<2.9.0" 150 | pyflakes = ">=2.4.0,<2.5.0" 151 | 152 | [[package]] 153 | name = "flake8-black" 154 | version = "0.2.3" 155 | description = "flake8 plugin to call black as a code style validator" 156 | category = "dev" 157 | optional = false 158 | python-versions = "*" 159 | 160 | [package.dependencies] 161 | black = "*" 162 | flake8 = ">=3.0.0" 163 | toml = "*" 164 | 165 | [[package]] 166 | name = "flake8-isort" 167 | version = "4.1.1" 168 | description = "flake8 plugin that integrates isort ." 169 | category = "dev" 170 | optional = false 171 | python-versions = "*" 172 | 173 | [package.dependencies] 174 | flake8 = ">=3.2.1,<5" 175 | isort = ">=4.3.5,<6" 176 | testfixtures = ">=6.8.0,<7" 177 | 178 | [package.extras] 179 | test = ["pytest-cov"] 180 | 181 | [[package]] 182 | name = "identify" 183 | version = "2.4.4" 184 | description = "File identification library for Python" 185 | category = "dev" 186 | optional = false 187 | python-versions = ">=3.6.1" 188 | 189 | [package.extras] 190 | license = ["ukkonen"] 191 | 192 | [[package]] 193 | name = "importlib-metadata" 194 | version = "4.2.0" 195 | description = "Read metadata from Python packages" 196 | category = "main" 197 | optional = false 198 | python-versions = ">=3.6" 199 | 200 | [package.dependencies] 201 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 202 | zipp = ">=0.5" 203 | 204 | [package.extras] 205 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 206 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 207 | 208 | [[package]] 209 | name = "importlib-resources" 210 | version = "5.2.3" 211 | description = "Read resources from Python packages" 212 | category = "dev" 213 | optional = false 214 | python-versions = ">=3.6" 215 | 216 | [package.dependencies] 217 | zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} 218 | 219 | [package.extras] 220 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 221 | testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] 222 | 223 | [[package]] 224 | name = "iniconfig" 225 | version = "1.1.1" 226 | description = "iniconfig: brain-dead simple config-ini parsing" 227 | category = "dev" 228 | optional = false 229 | python-versions = "*" 230 | 231 | [[package]] 232 | name = "isort" 233 | version = "5.10.1" 234 | description = "A Python utility / library to sort Python imports." 235 | category = "dev" 236 | optional = false 237 | python-versions = ">=3.6.1,<4.0" 238 | 239 | [package.extras] 240 | pipfile_deprecated_finder = ["pipreqs", "requirementslib"] 241 | requirements_deprecated_finder = ["pipreqs", "pip-api"] 242 | colors = ["colorama (>=0.4.3,<0.5.0)"] 243 | plugins = ["setuptools"] 244 | 245 | [[package]] 246 | name = "mccabe" 247 | version = "0.6.1" 248 | description = "McCabe checker, plugin for flake8" 249 | category = "dev" 250 | optional = false 251 | python-versions = "*" 252 | 253 | [[package]] 254 | name = "mypy-extensions" 255 | version = "0.4.3" 256 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 257 | category = "dev" 258 | optional = false 259 | python-versions = "*" 260 | 261 | [[package]] 262 | name = "nodeenv" 263 | version = "1.6.0" 264 | description = "Node.js virtual environment builder" 265 | category = "dev" 266 | optional = false 267 | python-versions = "*" 268 | 269 | [[package]] 270 | name = "packaging" 271 | version = "21.3" 272 | description = "Core utilities for Python packages" 273 | category = "main" 274 | optional = false 275 | python-versions = ">=3.6" 276 | 277 | [package.dependencies] 278 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 279 | 280 | [[package]] 281 | name = "pathspec" 282 | version = "0.9.0" 283 | description = "Utility library for gitignore style pattern matching of file paths." 284 | category = "dev" 285 | optional = false 286 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 287 | 288 | [[package]] 289 | name = "platformdirs" 290 | version = "2.4.0" 291 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 292 | category = "dev" 293 | optional = false 294 | python-versions = ">=3.6" 295 | 296 | [package.extras] 297 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 298 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 299 | 300 | [[package]] 301 | name = "pluggy" 302 | version = "1.0.0" 303 | description = "plugin and hook calling mechanisms for python" 304 | category = "dev" 305 | optional = false 306 | python-versions = ">=3.6" 307 | 308 | [package.dependencies] 309 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 310 | 311 | [package.extras] 312 | dev = ["pre-commit", "tox"] 313 | testing = ["pytest", "pytest-benchmark"] 314 | 315 | [[package]] 316 | name = "pre-commit" 317 | version = "2.17.0" 318 | description = "A framework for managing and maintaining multi-language pre-commit hooks." 319 | category = "dev" 320 | optional = false 321 | python-versions = ">=3.6.1" 322 | 323 | [package.dependencies] 324 | cfgv = ">=2.0.0" 325 | identify = ">=1.0.0" 326 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 327 | importlib-resources = {version = "<5.3", markers = "python_version < \"3.7\""} 328 | nodeenv = ">=0.11.1" 329 | pyyaml = ">=5.1" 330 | toml = "*" 331 | virtualenv = ">=20.0.8" 332 | 333 | [[package]] 334 | name = "py" 335 | version = "1.11.0" 336 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 337 | category = "dev" 338 | optional = false 339 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 340 | 341 | [[package]] 342 | name = "pycodestyle" 343 | version = "2.8.0" 344 | description = "Python style guide checker" 345 | category = "dev" 346 | optional = false 347 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 348 | 349 | [[package]] 350 | name = "pyflakes" 351 | version = "2.4.0" 352 | description = "passive checker of Python programs" 353 | category = "dev" 354 | optional = false 355 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 356 | 357 | [[package]] 358 | name = "pyparsing" 359 | version = "3.0.7" 360 | description = "Python parsing module" 361 | category = "main" 362 | optional = false 363 | python-versions = ">=3.6" 364 | 365 | [package.extras] 366 | diagrams = ["jinja2", "railroad-diagrams"] 367 | 368 | [[package]] 369 | name = "pytest" 370 | version = "6.2.5" 371 | description = "pytest: simple powerful testing with Python" 372 | category = "dev" 373 | optional = false 374 | python-versions = ">=3.6" 375 | 376 | [package.dependencies] 377 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 378 | attrs = ">=19.2.0" 379 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 380 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 381 | iniconfig = "*" 382 | packaging = "*" 383 | pluggy = ">=0.12,<2.0" 384 | py = ">=1.8.2" 385 | toml = "*" 386 | 387 | [package.extras] 388 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 389 | 390 | [[package]] 391 | name = "pytest-mock" 392 | version = "3.6.1" 393 | description = "Thin-wrapper around the mock package for easier use with pytest" 394 | category = "dev" 395 | optional = false 396 | python-versions = ">=3.6" 397 | 398 | [package.dependencies] 399 | pytest = ">=5.0" 400 | 401 | [package.extras] 402 | dev = ["pre-commit", "tox", "pytest-asyncio"] 403 | 404 | [[package]] 405 | name = "pyyaml" 406 | version = "6.0" 407 | description = "YAML parser and emitter for Python" 408 | category = "dev" 409 | optional = false 410 | python-versions = ">=3.6" 411 | 412 | [[package]] 413 | name = "regex" 414 | version = "2022.1.18" 415 | description = "Alternative regular expression module, to replace re." 416 | category = "dev" 417 | optional = false 418 | python-versions = "*" 419 | 420 | [[package]] 421 | name = "six" 422 | version = "1.16.0" 423 | description = "Python 2 and 3 compatibility utilities" 424 | category = "dev" 425 | optional = false 426 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 427 | 428 | [[package]] 429 | name = "testfixtures" 430 | version = "6.18.3" 431 | description = "A collection of helpers and mock objects for unit tests and doc tests." 432 | category = "dev" 433 | optional = false 434 | python-versions = "*" 435 | 436 | [package.extras] 437 | build = ["setuptools-git", "wheel", "twine"] 438 | docs = ["sphinx", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] 439 | test = ["pytest (>=3.6)", "pytest-cov", "pytest-django", "zope.component", "sybil", "twisted", "mock", "django (<2)", "django"] 440 | 441 | [[package]] 442 | name = "toml" 443 | version = "0.10.2" 444 | description = "Python Library for Tom's Obvious, Minimal Language" 445 | category = "dev" 446 | optional = false 447 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 448 | 449 | [[package]] 450 | name = "tomli" 451 | version = "1.2.3" 452 | description = "A lil' TOML parser" 453 | category = "dev" 454 | optional = false 455 | python-versions = ">=3.6" 456 | 457 | [[package]] 458 | name = "tomlkit" 459 | version = "0.8.0" 460 | description = "Style preserving TOML library" 461 | category = "main" 462 | optional = false 463 | python-versions = ">=3.6,<4.0" 464 | 465 | [[package]] 466 | name = "typed-ast" 467 | version = "1.5.1" 468 | description = "a fork of Python 2 and 3 ast modules with type comment support" 469 | category = "dev" 470 | optional = false 471 | python-versions = ">=3.6" 472 | 473 | [[package]] 474 | name = "typer" 475 | version = "0.4.0" 476 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 477 | category = "main" 478 | optional = false 479 | python-versions = ">=3.6" 480 | 481 | [package.dependencies] 482 | click = ">=7.1.1,<9.0.0" 483 | 484 | [package.extras] 485 | all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] 486 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] 487 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] 488 | test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)"] 489 | 490 | [[package]] 491 | name = "typing-extensions" 492 | version = "4.0.1" 493 | description = "Backported and Experimental Type Hints for Python 3.6+" 494 | category = "main" 495 | optional = false 496 | python-versions = ">=3.6" 497 | 498 | [[package]] 499 | name = "virtualenv" 500 | version = "20.13.0" 501 | description = "Virtual Python Environment builder" 502 | category = "dev" 503 | optional = false 504 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 505 | 506 | [package.dependencies] 507 | distlib = ">=0.3.1,<1" 508 | filelock = ">=3.2,<4" 509 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 510 | importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} 511 | platformdirs = ">=2,<3" 512 | six = ">=1.9.0,<2" 513 | 514 | [package.extras] 515 | docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] 516 | testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] 517 | 518 | [[package]] 519 | name = "zipp" 520 | version = "3.6.0" 521 | description = "Backport of pathlib-compatible object wrapper for zip files" 522 | category = "main" 523 | optional = false 524 | python-versions = ">=3.6" 525 | 526 | [package.extras] 527 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 528 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] 529 | 530 | [metadata] 531 | lock-version = "1.1" 532 | python-versions = "^3.6" 533 | content-hash = "2e1d0ce8081526306e9febaa392411946f549d220f119af9868af3cca44fd2d6" 534 | 535 | [metadata.files] 536 | appdirs = [ 537 | {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, 538 | {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, 539 | ] 540 | atomicwrites = [ 541 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 542 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 543 | ] 544 | attrs = [ 545 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 546 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 547 | ] 548 | black = [ 549 | {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, 550 | {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, 551 | {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, 552 | ] 553 | cfgv = [ 554 | {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, 555 | {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, 556 | ] 557 | click = [ 558 | {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, 559 | {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, 560 | ] 561 | colorama = [ 562 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 563 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 564 | ] 565 | dataclasses = [ 566 | {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, 567 | {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, 568 | ] 569 | distlib = [ 570 | {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, 571 | {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, 572 | ] 573 | filelock = [ 574 | {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"}, 575 | {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"}, 576 | ] 577 | flake8 = [ 578 | {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, 579 | {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, 580 | ] 581 | flake8-black = [ 582 | {file = "flake8-black-0.2.3.tar.gz", hash = "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684"}, 583 | {file = "flake8_black-0.2.3-py3-none-any.whl", hash = "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96"}, 584 | ] 585 | flake8-isort = [ 586 | {file = "flake8-isort-4.1.1.tar.gz", hash = "sha256:d814304ab70e6e58859bc5c3e221e2e6e71c958e7005239202fee19c24f82717"}, 587 | {file = "flake8_isort-4.1.1-py3-none-any.whl", hash = "sha256:c4e8b6dcb7be9b71a02e6e5d4196cefcef0f3447be51e82730fb336fff164949"}, 588 | ] 589 | identify = [ 590 | {file = "identify-2.4.4-py2.py3-none-any.whl", hash = "sha256:aa68609c7454dbcaae60a01ff6b8df1de9b39fe6e50b1f6107ec81dcda624aa6"}, 591 | {file = "identify-2.4.4.tar.gz", hash = "sha256:6b4b5031f69c48bf93a646b90de9b381c6b5f560df4cbe0ed3cf7650ae741e4d"}, 592 | ] 593 | importlib-metadata = [ 594 | {file = "importlib_metadata-4.2.0-py3-none-any.whl", hash = "sha256:057e92c15bc8d9e8109738a48db0ccb31b4d9d5cfbee5a8670879a30be66304b"}, 595 | {file = "importlib_metadata-4.2.0.tar.gz", hash = "sha256:b7e52a1f8dec14a75ea73e0891f3060099ca1d8e6a462a4dff11c3e119ea1b31"}, 596 | ] 597 | importlib-resources = [ 598 | {file = "importlib_resources-5.2.3-py3-none-any.whl", hash = "sha256:ae35ed1cfe8c0d6c1a53ecd168167f01fa93b893d51a62cdf23aea044c67211b"}, 599 | {file = "importlib_resources-5.2.3.tar.gz", hash = "sha256:203d70dda34cfbfbb42324a8d4211196e7d3e858de21a5eb68c6d1cdd99e4e98"}, 600 | ] 601 | iniconfig = [ 602 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 603 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 604 | ] 605 | isort = [ 606 | {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, 607 | {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, 608 | ] 609 | mccabe = [ 610 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 611 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 612 | ] 613 | mypy-extensions = [ 614 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 615 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 616 | ] 617 | nodeenv = [ 618 | {file = "nodeenv-1.6.0-py2.py3-none-any.whl", hash = "sha256:621e6b7076565ddcacd2db0294c0381e01fd28945ab36bcf00f41c5daf63bef7"}, 619 | {file = "nodeenv-1.6.0.tar.gz", hash = "sha256:3ef13ff90291ba2a4a7a4ff9a979b63ffdd00a464dbe04acf0ea6471517a4c2b"}, 620 | ] 621 | packaging = [ 622 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 623 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 624 | ] 625 | pathspec = [ 626 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 627 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 628 | ] 629 | platformdirs = [ 630 | {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, 631 | {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, 632 | ] 633 | pluggy = [ 634 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 635 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 636 | ] 637 | pre-commit = [ 638 | {file = "pre_commit-2.17.0-py2.py3-none-any.whl", hash = "sha256:725fa7459782d7bec5ead072810e47351de01709be838c2ce1726b9591dad616"}, 639 | {file = "pre_commit-2.17.0.tar.gz", hash = "sha256:c1a8040ff15ad3d648c70cc3e55b93e4d2d5b687320955505587fd79bbaed06a"}, 640 | ] 641 | py = [ 642 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 643 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 644 | ] 645 | pycodestyle = [ 646 | {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, 647 | {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, 648 | ] 649 | pyflakes = [ 650 | {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, 651 | {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, 652 | ] 653 | pyparsing = [ 654 | {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, 655 | {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, 656 | ] 657 | pytest = [ 658 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 659 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 660 | ] 661 | pytest-mock = [ 662 | {file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"}, 663 | {file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"}, 664 | ] 665 | pyyaml = [ 666 | {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, 667 | {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, 668 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, 669 | {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, 670 | {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"}, 671 | {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, 672 | {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, 673 | {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, 674 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, 675 | {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, 676 | {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"}, 677 | {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, 678 | {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, 679 | {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, 680 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, 681 | {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, 682 | {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"}, 683 | {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, 684 | {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, 685 | {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, 686 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, 687 | {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, 688 | {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"}, 689 | {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, 690 | {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, 691 | {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, 692 | {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, 693 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, 694 | {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, 695 | {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"}, 696 | {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, 697 | {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, 698 | {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, 699 | ] 700 | regex = [ 701 | {file = "regex-2022.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34316bf693b1d2d29c087ee7e4bb10cdfa39da5f9c50fa15b07489b4ab93a1b5"}, 702 | {file = "regex-2022.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a0b9f6a1a15d494b35f25ed07abda03209fa76c33564c09c9e81d34f4b919d7"}, 703 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f99112aed4fb7cee00c7f77e8b964a9b10f69488cdff626ffd797d02e2e4484f"}, 704 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a2bf98ac92f58777c0fafc772bf0493e67fcf677302e0c0a630ee517a43b949"}, 705 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8618d9213a863c468a865e9d2ec50221015f7abf52221bc927152ef26c484b4c"}, 706 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b52cc45e71657bc4743a5606d9023459de929b2a198d545868e11898ba1c3f59"}, 707 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e12949e5071c20ec49ef00c75121ed2b076972132fc1913ddf5f76cae8d10b4"}, 708 | {file = "regex-2022.1.18-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b02e3e72665cd02afafb933453b0c9f6c59ff6e3708bd28d0d8580450e7e88af"}, 709 | {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abfcb0ef78df0ee9df4ea81f03beea41849340ce33a4c4bd4dbb99e23ec781b6"}, 710 | {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6213713ac743b190ecbf3f316d6e41d099e774812d470422b3a0f137ea635832"}, 711 | {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:61ebbcd208d78658b09e19c78920f1ad38936a0aa0f9c459c46c197d11c580a0"}, 712 | {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b013f759cd69cb0a62de954d6d2096d648bc210034b79b1881406b07ed0a83f9"}, 713 | {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9187500d83fd0cef4669385cbb0961e227a41c0c9bc39219044e35810793edf7"}, 714 | {file = "regex-2022.1.18-cp310-cp310-win32.whl", hash = "sha256:94c623c331a48a5ccc7d25271399aff29729fa202c737ae3b4b28b89d2b0976d"}, 715 | {file = "regex-2022.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:1a171eaac36a08964d023eeff740b18a415f79aeb212169080c170ec42dd5184"}, 716 | {file = "regex-2022.1.18-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:49810f907dfe6de8da5da7d2b238d343e6add62f01a15d03e2195afc180059ed"}, 717 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d2f5c3f7057530afd7b739ed42eb04f1011203bc5e4663e1e1d01bb50f813e3"}, 718 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85ffd6b1cb0dfb037ede50ff3bef80d9bf7fa60515d192403af6745524524f3b"}, 719 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba37f11e1d020969e8a779c06b4af866ffb6b854d7229db63c5fdddfceaa917f"}, 720 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e27ea1ebe4a561db75a880ac659ff439dec7f55588212e71700bb1ddd5af9"}, 721 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37978254d9d00cda01acc1997513f786b6b971e57b778fbe7c20e30ae81a97f3"}, 722 | {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54a1eb9fd38f2779e973d2f8958fd575b532fe26013405d1afb9ee2374e7ab8"}, 723 | {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:768632fd8172ae03852e3245f11c8a425d95f65ff444ce46b3e673ae5b057b74"}, 724 | {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:de2923886b5d3214be951bc2ce3f6b8ac0d6dfd4a0d0e2a4d2e5523d8046fdfb"}, 725 | {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1333b3ce73269f986b1fa4d5d395643810074dc2de5b9d262eb258daf37dc98f"}, 726 | {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:d19a34f8a3429bd536996ad53597b805c10352a8561d8382e05830df389d2b43"}, 727 | {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d2f355a951f60f0843f2368b39970e4667517e54e86b1508e76f92b44811a8a"}, 728 | {file = "regex-2022.1.18-cp36-cp36m-win32.whl", hash = "sha256:2245441445099411b528379dee83e56eadf449db924648e5feb9b747473f42e3"}, 729 | {file = "regex-2022.1.18-cp36-cp36m-win_amd64.whl", hash = "sha256:25716aa70a0d153cd844fe861d4f3315a6ccafce22b39d8aadbf7fcadff2b633"}, 730 | {file = "regex-2022.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7e070d3aef50ac3856f2ef5ec7214798453da878bb5e5a16c16a61edf1817cc3"}, 731 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22709d701e7037e64dae2a04855021b62efd64a66c3ceed99dfd684bfef09e38"}, 732 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9099bf89078675c372339011ccfc9ec310310bf6c292b413c013eb90ffdcafc"}, 733 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04611cc0f627fc4a50bc4a9a2e6178a974c6a6a4aa9c1cca921635d2c47b9c87"}, 734 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:552a39987ac6655dad4bf6f17dd2b55c7b0c6e949d933b8846d2e312ee80005a"}, 735 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e031899cb2bc92c0cf4d45389eff5b078d1936860a1be3aa8c94fa25fb46ed8"}, 736 | {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2dacb3dae6b8cc579637a7b72f008bff50a94cde5e36e432352f4ca57b9e54c4"}, 737 | {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e5c31d70a478b0ca22a9d2d76d520ae996214019d39ed7dd93af872c7f301e52"}, 738 | {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bb804c7d0bfbd7e3f33924ff49757de9106c44e27979e2492819c16972ec0da2"}, 739 | {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:36b2d700a27e168fa96272b42d28c7ac3ff72030c67b32f37c05616ebd22a202"}, 740 | {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:16f81025bb3556eccb0681d7946e2b35ff254f9f888cff7d2120e8826330315c"}, 741 | {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:da80047524eac2acf7c04c18ac7a7da05a9136241f642dd2ed94269ef0d0a45a"}, 742 | {file = "regex-2022.1.18-cp37-cp37m-win32.whl", hash = "sha256:6ca45359d7a21644793de0e29de497ef7f1ae7268e346c4faf87b421fea364e6"}, 743 | {file = "regex-2022.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:38289f1690a7e27aacd049e420769b996826f3728756859420eeee21cc857118"}, 744 | {file = "regex-2022.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6014038f52b4b2ac1fa41a58d439a8a00f015b5c0735a0cd4b09afe344c94899"}, 745 | {file = "regex-2022.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b5d6f9aed3153487252d00a18e53f19b7f52a1651bc1d0c4b5844bc286dfa52"}, 746 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d24b03daf7415f78abc2d25a208f234e2c585e5e6f92f0204d2ab7b9ab48e3"}, 747 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf594cc7cc9d528338d66674c10a5b25e3cde7dd75c3e96784df8f371d77a298"}, 748 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd914db437ec25bfa410f8aa0aa2f3ba87cdfc04d9919d608d02330947afaeab"}, 749 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b6840b6448203228a9d8464a7a0d99aa8fa9f027ef95fe230579abaf8a6ee1"}, 750 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11772be1eb1748e0e197a40ffb82fb8fd0d6914cd147d841d9703e2bef24d288"}, 751 | {file = "regex-2022.1.18-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a602bdc8607c99eb5b391592d58c92618dcd1537fdd87df1813f03fed49957a6"}, 752 | {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7e26eac9e52e8ce86f915fd33380f1b6896a2b51994e40bb094841e5003429b4"}, 753 | {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:519c0b3a6fbb68afaa0febf0d28f6c4b0a1074aefc484802ecb9709faf181607"}, 754 | {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3c7ea86b9ca83e30fa4d4cd0eaf01db3ebcc7b2726a25990966627e39577d729"}, 755 | {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:51f02ca184518702975b56affde6c573ebad4e411599005ce4468b1014b4786c"}, 756 | {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:385ccf6d011b97768a640e9d4de25412204fbe8d6b9ae39ff115d4ff03f6fe5d"}, 757 | {file = "regex-2022.1.18-cp38-cp38-win32.whl", hash = "sha256:1f8c0ae0a0de4e19fddaaff036f508db175f6f03db318c80bbc239a1def62d02"}, 758 | {file = "regex-2022.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:760c54ad1b8a9b81951030a7e8e7c3ec0964c1cb9fee585a03ff53d9e531bb8e"}, 759 | {file = "regex-2022.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:93c20777a72cae8620203ac11c4010365706062aa13aaedd1a21bb07adbb9d5d"}, 760 | {file = "regex-2022.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6aa427c55a0abec450bca10b64446331b5ca8f79b648531138f357569705bc4a"}, 761 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38baee6bdb7fe1b110b6b3aaa555e6e872d322206b7245aa39572d3fc991ee4"}, 762 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:752e7ddfb743344d447367baa85bccd3629c2c3940f70506eb5f01abce98ee68"}, 763 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8acef4d8a4353f6678fd1035422a937c2170de58a2b29f7da045d5249e934101"}, 764 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c73d2166e4b210b73d1429c4f1ca97cea9cc090e5302df2a7a0a96ce55373f1c"}, 765 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24c89346734a4e4d60ecf9b27cac4c1fee3431a413f7aa00be7c4d7bbacc2c4d"}, 766 | {file = "regex-2022.1.18-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:596f5ae2eeddb79b595583c2e0285312b2783b0ec759930c272dbf02f851ff75"}, 767 | {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ecfe51abf7f045e0b9cdde71ca9e153d11238679ef7b5da6c82093874adf3338"}, 768 | {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1d6301f5288e9bdca65fab3de6b7de17362c5016d6bf8ee4ba4cbe833b2eda0f"}, 769 | {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:93cce7d422a0093cfb3606beae38a8e47a25232eea0f292c878af580a9dc7605"}, 770 | {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cf0db26a1f76aa6b3aa314a74b8facd586b7a5457d05b64f8082a62c9c49582a"}, 771 | {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:defa0652696ff0ba48c8aff5a1fac1eef1ca6ac9c660b047fc8e7623c4eb5093"}, 772 | {file = "regex-2022.1.18-cp39-cp39-win32.whl", hash = "sha256:6db1b52c6f2c04fafc8da17ea506608e6be7086715dab498570c3e55e4f8fbd1"}, 773 | {file = "regex-2022.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:ebaeb93f90c0903233b11ce913a7cb8f6ee069158406e056f884854c737d2442"}, 774 | {file = "regex-2022.1.18.tar.gz", hash = "sha256:97f32dc03a8054a4c4a5ab5d761ed4861e828b2c200febd4e46857069a483916"}, 775 | ] 776 | six = [ 777 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 778 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 779 | ] 780 | testfixtures = [ 781 | {file = "testfixtures-6.18.3-py2.py3-none-any.whl", hash = "sha256:6ddb7f56a123e1a9339f130a200359092bd0a6455e31838d6c477e8729bb7763"}, 782 | {file = "testfixtures-6.18.3.tar.gz", hash = "sha256:2600100ae96ffd082334b378e355550fef8b4a529a6fa4c34f47130905c7426d"}, 783 | ] 784 | toml = [ 785 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 786 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 787 | ] 788 | tomli = [ 789 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 790 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 791 | ] 792 | tomlkit = [ 793 | {file = "tomlkit-0.8.0-py3-none-any.whl", hash = "sha256:b824e3466f1d475b2b5f1c392954c6cb7ea04d64354ff7300dc7c14257dc85db"}, 794 | {file = "tomlkit-0.8.0.tar.gz", hash = "sha256:29e84a855712dfe0e88a48f6d05c21118dbafb283bb2eed614d46f80deb8e9a1"}, 795 | ] 796 | typed-ast = [ 797 | {file = "typed_ast-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8314c92414ce7481eee7ad42b353943679cf6f30237b5ecbf7d835519e1212"}, 798 | {file = "typed_ast-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b53ae5de5500529c76225d18eeb060efbcec90ad5e030713fe8dab0fb4531631"}, 799 | {file = "typed_ast-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24058827d8f5d633f97223f5148a7d22628099a3d2efe06654ce872f46f07cdb"}, 800 | {file = "typed_ast-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6d495c1ef572519a7bac9534dbf6d94c40e5b6a608ef41136133377bba4aa08"}, 801 | {file = "typed_ast-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e"}, 802 | {file = "typed_ast-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:256115a5bc7ea9e665c6314ed6671ee2c08ca380f9d5f130bd4d2c1f5848d695"}, 803 | {file = "typed_ast-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7c42707ab981b6cf4b73490c16e9d17fcd5227039720ca14abe415d39a173a30"}, 804 | {file = "typed_ast-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:71dcda943a471d826ea930dd449ac7e76db7be778fcd722deb63642bab32ea3f"}, 805 | {file = "typed_ast-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4f30a2bcd8e68adbb791ce1567fdb897357506f7ea6716f6bbdd3053ac4d9471"}, 806 | {file = "typed_ast-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb"}, 807 | {file = "typed_ast-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9caaf2b440efb39ecbc45e2fabde809cbe56272719131a6318fd9bf08b58e2cb"}, 808 | {file = "typed_ast-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9bcad65d66d594bffab8575f39420fe0ee96f66e23c4d927ebb4e24354ec1af"}, 809 | {file = "typed_ast-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:591bc04e507595887160ed7aa8d6785867fb86c5793911be79ccede61ae96f4d"}, 810 | {file = "typed_ast-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:a80d84f535642420dd17e16ae25bb46c7f4c16ee231105e7f3eb43976a89670a"}, 811 | {file = "typed_ast-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:38cf5c642fa808300bae1281460d4f9b7617cf864d4e383054a5ef336e344d32"}, 812 | {file = "typed_ast-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b6ab14c56bc9c7e3c30228a0a0b54b915b1579613f6e463ba6f4eb1382e7fd4"}, 813 | {file = "typed_ast-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2b8d7007f6280e36fa42652df47087ac7b0a7d7f09f9468f07792ba646aac2d"}, 814 | {file = "typed_ast-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b6d17f37f6edd879141e64a5db17b67488cfeffeedad8c5cec0392305e9bc775"}, 815 | {file = "typed_ast-1.5.1.tar.gz", hash = "sha256:484137cab8ecf47e137260daa20bafbba5f4e3ec7fda1c1e69ab299b75fa81c5"}, 816 | ] 817 | typer = [ 818 | {file = "typer-0.4.0-py3-none-any.whl", hash = "sha256:d81169725140423d072df464cad1ff25ee154ef381aaf5b8225352ea187ca338"}, 819 | {file = "typer-0.4.0.tar.gz", hash = "sha256:63c3aeab0549750ffe40da79a1b524f60e08a2cbc3126c520ebf2eeaf507f5dd"}, 820 | ] 821 | typing-extensions = [ 822 | {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, 823 | {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, 824 | ] 825 | virtualenv = [ 826 | {file = "virtualenv-20.13.0-py2.py3-none-any.whl", hash = "sha256:339f16c4a86b44240ba7223d0f93a7887c3ca04b5f9c8129da7958447d079b09"}, 827 | {file = "virtualenv-20.13.0.tar.gz", hash = "sha256:d8458cf8d59d0ea495ad9b34c2599487f8a7772d796f9910858376d1600dd2dd"}, 828 | ] 829 | zipp = [ 830 | {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, 831 | {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, 832 | ] 833 | --------------------------------------------------------------------------------