├── tests ├── __init__.py ├── test_parser.py └── test_maho.py ├── mypy_silent ├── __init__.py ├── py.typed ├── utils.py ├── maho.py ├── parser.py └── cli.py ├── .prettierrc.json ├── .github ├── renovate.json └── workflows │ └── push.yml ├── mypy.ini ├── pyproject.toml ├── .pre-commit-config.yaml ├── LICENSE ├── README.md ├── .gitignore └── poetry.lock /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mypy_silent/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /mypy_silent/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | "@whtsky/prettier-config" 2 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["@whtsky"] 3 | } 4 | -------------------------------------------------------------------------------- /mypy_silent/utils.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from typing import Iterable 3 | from typing import Optional 4 | 5 | 6 | def get_lines(mypy_output_file: Optional[str]) -> Iterable[str]: 7 | if not mypy_output_file: 8 | return sys.stdin 9 | with open(mypy_output_file) as f: 10 | return f.readlines() 11 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | check_untyped_defs = True 3 | disallow_untyped_calls = True 4 | disallow_untyped_defs = True 5 | follow_imports = normal 6 | ignore_missing_imports = True 7 | no_implicit_optional = True 8 | python_version = 3.6 9 | strict_equality = True 10 | strict_optional = True 11 | warn_no_return = True 12 | warn_redundant_casts = True 13 | warn_unreachable = True 14 | warn_unused_configs = True 15 | warn_unused_ignores = True 16 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | authors = ["Wu Haotian "] 3 | description = "Silence mypy by adding or removing code comments" 4 | license = "MIT" 5 | name = "mypy-silent" 6 | packages = [ 7 | {include = "mypy_silent"}, 8 | {include = "mypy_silent/py.typed"}, 9 | ] 10 | version = "0.4.0" 11 | readme = "README.md" 12 | 13 | [tool.poetry.dependencies] 14 | python = "^3.6" 15 | typer = "^0.3.2" 16 | typing-extensions = "^3.7.4;python<3.8" 17 | 18 | [tool.poetry.dev-dependencies] 19 | mypy = "^0.812" 20 | pytest = "^6.2.5" 21 | pytest-cov = "^3.0.0" 22 | 23 | [build-system] 24 | build-backend = "poetry.core.masonry.api" 25 | requires = ["poetry-core>=1.0.0"] 26 | 27 | [tool.poetry.scripts] 28 | mypy-silent = "mypy_silent.cli:cli" 29 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/asottile/reorder_python_imports 3 | rev: v3.0.1 4 | hooks: 5 | - id: reorder-python-imports 6 | - repo: https://github.com/asottile/pyupgrade 7 | rev: v2.31.1 8 | hooks: 9 | - id: pyupgrade 10 | args: ['--py36-plus'] 11 | - repo: https://github.com/psf/black 12 | rev: 22.3.0 13 | hooks: 14 | - id: black 15 | language_version: python3 16 | - repo: https://github.com/pre-commit/pre-commit-hooks 17 | rev: v4.1.0 18 | hooks: 19 | - id: fix-byte-order-marker 20 | - id: trailing-whitespace 21 | - id: end-of-file-fixer 22 | - repo: https://github.com/pre-commit/mirrors-prettier 23 | rev: 'v2.6.2' 24 | hooks: 25 | - id: prettier 26 | types: [file] 27 | files: \.(js|jsx|ts|tsx|yaml|yml|json|json5)$ 28 | additional_dependencies: 29 | - prettier 30 | - '@whtsky/prettier-config' 31 | -------------------------------------------------------------------------------- /mypy_silent/maho.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import Optional 3 | 4 | 5 | _type_ignore_re = re.compile(r"# type: ignore(\[[a-z, \-]+\])?") 6 | 7 | 8 | def add_type_ignore_comment(line: str, error_code: Optional[str]) -> str: 9 | # Workarounds for https://mypy.readthedocs.io/en/stable/common_issues.html#silencing-linters 10 | 11 | type_ignore_comment = "# type: ignore" 12 | 13 | if error_code: 14 | type_ignore_comment += f"[{error_code}]" 15 | 16 | if "# noqa" in line: 17 | return line.replace("# noqa", f"{type_ignore_comment} # noqa", 1) 18 | content_without_crlf = line.rstrip("\r\n") 19 | return ( 20 | content_without_crlf 21 | + f" {type_ignore_comment}" 22 | + line[len(content_without_crlf) :] 23 | ) 24 | 25 | 26 | def remove_type_ignore_comment(line: str) -> str: 27 | content_without_crlf = line.rstrip("\r\n") 28 | return ( 29 | _type_ignore_re.sub("#", content_without_crlf).rstrip().rstrip("#").rstrip() 30 | + line[len(content_without_crlf) :] 31 | ) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Wu Haotian 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 | -------------------------------------------------------------------------------- /tests/test_parser.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | import pytest 4 | 5 | from mypy_silent.parser import FilePosition 6 | from mypy_silent.parser import get_info_from_mypy_output 7 | from mypy_silent.parser import MypyMessage 8 | 9 | 10 | @pytest.mark.parametrize( 11 | "input,output", 12 | ( 13 | (["test"], []), 14 | ( 15 | [ 16 | "utils/template.py:49: error: Cannot assign to a method", 17 | "utils/template.py:53: error: Statement is unreachable [unreachable]", 18 | ], 19 | [ 20 | MypyMessage( 21 | position=FilePosition(filename="utils/template.py", line=49), 22 | message="error: Cannot assign to a method", 23 | error_code=None, 24 | ), 25 | MypyMessage( 26 | position=FilePosition(filename="utils/template.py", line=53), 27 | message="error: Statement is unreachable", 28 | error_code="unreachable", 29 | ), 30 | ], 31 | ), 32 | ), 33 | ) 34 | def test_get_info_from_mypy_output(input: List[str], output: List[MypyMessage]) -> None: 35 | assert list(get_info_from_mypy_output(input)) == output 36 | -------------------------------------------------------------------------------- /mypy_silent/parser.py: -------------------------------------------------------------------------------- 1 | import re 2 | from typing import FrozenSet 3 | from typing import Iterable 4 | from typing import NamedTuple 5 | from typing import Optional 6 | 7 | from typing_extensions import Final 8 | 9 | UNUSED_IGNORE_MESSAGES: Final[FrozenSet[str]] = frozenset( 10 | {"error: unused 'type: ignore' comment", 'error: unused "type: ignore" comment'} 11 | ) 12 | 13 | 14 | class FilePosition(NamedTuple): 15 | filename: str 16 | line: int 17 | 18 | 19 | class MypyMessage(NamedTuple): 20 | position: FilePosition 21 | message: str 22 | error_code: Optional[str] 23 | 24 | 25 | _mypy_output_re = re.compile( 26 | r"^(?P[^:]+):(?P\d+):(?P.+?)(\[(?P[a-z-]+)\])?$" 27 | ) 28 | 29 | 30 | def get_info_from_mypy_output(lines: Iterable[str]) -> Iterable[MypyMessage]: 31 | for line in lines: 32 | line = line.strip() 33 | match = _mypy_output_re.match(line) 34 | if match: 35 | yield MypyMessage( 36 | position=FilePosition( 37 | filename=match.group("filename").strip(), 38 | line=int(match.group("line")), 39 | ), 40 | message=match.group("message").strip(), 41 | error_code=match.group("error_code"), 42 | ) 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mypy-silent 2 | 3 | Automatically add or remove `# type: ignore` commends to silence [mypy](https://github.com/python/mypy). Inspired by [pylint-silent](https://github.com/udifuchs/pylint-silent/) 4 | 5 | ## Why? 6 | 7 | Imagine you want to add type check for a legacy code base which has thounds of exisiting mypy errors. 8 | Instead of trying to fix all the exisiting errors, `mypy-silent` allows you to ignore the exisiting errors and adopt type checking right now. 9 | 10 | Although the exisiting errors are ignored, all the new code are type checked -- so you can moving towards fully type checked step by step. 11 | 12 | ## Install & Usage 13 | WARNING: `mypy-silent` modifies files **in place**. You should use some version control system ( like git ) to prevent losing codes. 14 | ```bash 15 | pip install mypy-silent 16 | 17 | mypy . # Whoa, lots of type error! 18 | mypy . | mypy-silent # mypy-silent will add or remove `# type: ignore` commends to your code 19 | mypy . # mypy should report 0 errors now. 20 | ``` 21 | 22 | ## Changelog 23 | 24 | ### v0.4.0 25 | 26 | - Add support for removing unused type ignores with error codes [#42](https://github.com/whtsky/mypy-silent/pull/42) 27 | 28 | ### v0.3.0 29 | 30 | - Add error code to ignore message comment.Instead of just adding `# type: ignore` we now add `# type: ignore[misc]` [#41](https://github.com/whtsky/mypy-silent/pull/41) 31 | 32 | ### v0.2.1 33 | 34 | - Fix import error on Python < 3.8 35 | 36 | ### v0.2.0 37 | 38 | - Support parsing mypy >=0.900 messages 39 | 40 | ### v0.1.0 41 | 42 | - Initial release 43 | -------------------------------------------------------------------------------- /.github/workflows/push.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | pull_request: 4 | name: Main workflow 5 | jobs: 6 | twine_check: 7 | name: twine check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@master 11 | - uses: actions/setup-python@v4 12 | with: 13 | python-version: '3.10' 14 | architecture: 'x64' 15 | - uses: snok/install-poetry@v1.3.1 16 | - run: poetry run pip install twine 17 | - run: poetry build 18 | - run: poetry run twine check --strict dist/* 19 | 20 | mypy: 21 | name: mypy 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@master 25 | - uses: actions/setup-python@v4 26 | with: 27 | python-version: '3.10' 28 | architecture: 'x64' 29 | - uses: snok/install-poetry@v1.3.1 30 | - run: poetry install 31 | - run: poetry run mypy . 32 | 33 | pytest: 34 | name: pytest ${{ matrix.python-version }} 35 | runs-on: ubuntu-latest 36 | strategy: 37 | matrix: 38 | python-version: ['3.6', '3.7', '3.8', '3.9', '3.10'] 39 | steps: 40 | - uses: actions/checkout@master 41 | - uses: actions/setup-python@v4 42 | with: 43 | python-version: ${{ matrix.python-version }} 44 | architecture: 'x64' 45 | - uses: snok/install-poetry@v1.3.1 46 | - run: poetry install 47 | - run: poetry run pytest --cov=mypy_silent --cov-report=xml 48 | - uses: codecov/codecov-action@v3.1.0 49 | with: 50 | token: ${{secrets.CODECOV_TOKEN}} 51 | -------------------------------------------------------------------------------- /mypy_silent/cli.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | from typing import Set 3 | 4 | import typer 5 | 6 | from mypy_silent.maho import add_type_ignore_comment 7 | from mypy_silent.maho import remove_type_ignore_comment 8 | from mypy_silent.parser import FilePosition 9 | from mypy_silent.parser import get_info_from_mypy_output 10 | from mypy_silent.parser import UNUSED_IGNORE_MESSAGES 11 | from mypy_silent.utils import get_lines 12 | 13 | 14 | def mypy_silent( 15 | mypy_output_file: Optional[str] = typer.Argument( 16 | None, help="Read mypy output from given file. Defaults to read from stdin" 17 | ), 18 | ) -> None: 19 | lines = get_lines(mypy_output_file) 20 | infos = get_info_from_mypy_output(lines) 21 | processed: Set[FilePosition] = set() 22 | for info in infos: 23 | if info.position in processed: 24 | continue 25 | with open(info.position.filename) as f: 26 | file_contents = f.readlines() 27 | 28 | old_content = file_contents[info.position.line - 1] 29 | if info.message in UNUSED_IGNORE_MESSAGES: 30 | new_content = remove_type_ignore_comment(old_content) 31 | else: 32 | new_content = add_type_ignore_comment( 33 | old_content, error_code=info.error_code 34 | ) 35 | file_contents[info.position.line - 1] = new_content 36 | with open(info.position.filename, "w") as f: 37 | f.writelines(file_contents) 38 | processed.add(info.position) 39 | 40 | 41 | def cli() -> None: 42 | typer.run(mypy_silent) 43 | 44 | 45 | if __name__ == "__main__": 46 | cli() 47 | -------------------------------------------------------------------------------- /.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 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # PyCharm 107 | .idea/ 108 | -------------------------------------------------------------------------------- /tests/test_maho.py: -------------------------------------------------------------------------------- 1 | from typing import Optional 2 | 3 | import pytest 4 | 5 | from mypy_silent.maho import add_type_ignore_comment 6 | from mypy_silent.maho import remove_type_ignore_comment 7 | 8 | 9 | @pytest.mark.parametrize( 10 | "input,error_code,output", 11 | ( 12 | ( 13 | " host, port, protocol = m.groups()\r\n", 14 | None, 15 | " host, port, protocol = m.groups() # type: ignore\r\n", 16 | ), 17 | ( 18 | " host, port, protocol = m.groups()\r\n", 19 | "misc", 20 | " host, port, protocol = m.groups() # type: ignore[misc]\r\n", 21 | ), 22 | ("print(a(2, 's')) # noqa", None, "print(a(2, 's')) # type: ignore # noqa"), 23 | ( 24 | "print(a(2, 's')) # noqa", 25 | "misc", 26 | "print(a(2, 's')) # type: ignore[misc] # noqa", 27 | ), 28 | ), 29 | ) 30 | def test_add_type_ignore_comment( 31 | input: str, error_code: Optional[str], output: str 32 | ) -> None: 33 | assert add_type_ignore_comment(input, error_code=error_code) == output 34 | 35 | 36 | @pytest.mark.parametrize( 37 | "input,output", 38 | ( 39 | ( 40 | " host, port, protocol = m.groups() # type: ignore\r\n", 41 | " host, port, protocol = m.groups()\r\n", 42 | ), 43 | ( 44 | " host, port, protocol = m.groups() # type: ignore[misc]\r\n", 45 | " host, port, protocol = m.groups()\r\n", 46 | ), 47 | ("# type: ignore. very good", "#. very good"), 48 | ("# type: ignore[misc]. very good", "#. very good"), 49 | ("# type: ignore[misc, arg-type]. very good", "#. very good"), 50 | ), 51 | ) 52 | def test_remove_type_ignore_comment(input: str, output: str) -> None: 53 | assert remove_type_ignore_comment(input) == output 54 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "atomicwrites" 3 | version = "1.4.0" 4 | description = "Atomic file writes." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [[package]] 10 | name = "attrs" 11 | version = "21.2.0" 12 | description = "Classes Without Boilerplate" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 16 | 17 | [package.extras] 18 | 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"] 19 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"] 21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"] 22 | 23 | [[package]] 24 | name = "click" 25 | version = "7.1.2" 26 | description = "Composable command line interface toolkit" 27 | category = "main" 28 | optional = false 29 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 30 | 31 | [[package]] 32 | name = "colorama" 33 | version = "0.4.4" 34 | description = "Cross-platform colored terminal text." 35 | category = "dev" 36 | optional = false 37 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 38 | 39 | [[package]] 40 | name = "coverage" 41 | version = "6.2" 42 | description = "Code coverage measurement for Python" 43 | category = "dev" 44 | optional = false 45 | python-versions = ">=3.6" 46 | 47 | [package.dependencies] 48 | tomli = {version = "*", optional = true, markers = "extra == \"toml\""} 49 | 50 | [package.extras] 51 | toml = ["tomli"] 52 | 53 | [[package]] 54 | name = "importlib-metadata" 55 | version = "4.6.1" 56 | description = "Read metadata from Python packages" 57 | category = "dev" 58 | optional = false 59 | python-versions = ">=3.6" 60 | 61 | [package.dependencies] 62 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 63 | zipp = ">=0.5" 64 | 65 | [package.extras] 66 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 67 | perf = ["ipython"] 68 | testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] 69 | 70 | [[package]] 71 | name = "iniconfig" 72 | version = "1.1.1" 73 | description = "iniconfig: brain-dead simple config-ini parsing" 74 | category = "dev" 75 | optional = false 76 | python-versions = "*" 77 | 78 | [[package]] 79 | name = "mypy" 80 | version = "0.812" 81 | description = "Optional static typing for Python" 82 | category = "dev" 83 | optional = false 84 | python-versions = ">=3.5" 85 | 86 | [package.dependencies] 87 | mypy-extensions = ">=0.4.3,<0.5.0" 88 | typed-ast = ">=1.4.0,<1.5.0" 89 | typing-extensions = ">=3.7.4" 90 | 91 | [package.extras] 92 | dmypy = ["psutil (>=4.0)"] 93 | 94 | [[package]] 95 | name = "mypy-extensions" 96 | version = "0.4.3" 97 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 98 | category = "dev" 99 | optional = false 100 | python-versions = "*" 101 | 102 | [[package]] 103 | name = "packaging" 104 | version = "21.0" 105 | description = "Core utilities for Python packages" 106 | category = "dev" 107 | optional = false 108 | python-versions = ">=3.6" 109 | 110 | [package.dependencies] 111 | pyparsing = ">=2.0.2" 112 | 113 | [[package]] 114 | name = "pluggy" 115 | version = "0.13.1" 116 | description = "plugin and hook calling mechanisms for python" 117 | category = "dev" 118 | optional = false 119 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 120 | 121 | [package.dependencies] 122 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 123 | 124 | [package.extras] 125 | dev = ["pre-commit", "tox"] 126 | 127 | [[package]] 128 | name = "py" 129 | version = "1.10.0" 130 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 131 | category = "dev" 132 | optional = false 133 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 134 | 135 | [[package]] 136 | name = "pyparsing" 137 | version = "2.4.7" 138 | description = "Python parsing module" 139 | category = "dev" 140 | optional = false 141 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 142 | 143 | [[package]] 144 | name = "pytest" 145 | version = "6.2.5" 146 | description = "pytest: simple powerful testing with Python" 147 | category = "dev" 148 | optional = false 149 | python-versions = ">=3.6" 150 | 151 | [package.dependencies] 152 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 153 | attrs = ">=19.2.0" 154 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 155 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 156 | iniconfig = "*" 157 | packaging = "*" 158 | pluggy = ">=0.12,<2.0" 159 | py = ">=1.8.2" 160 | toml = "*" 161 | 162 | [package.extras] 163 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 164 | 165 | [[package]] 166 | name = "pytest-cov" 167 | version = "3.0.0" 168 | description = "Pytest plugin for measuring coverage." 169 | category = "dev" 170 | optional = false 171 | python-versions = ">=3.6" 172 | 173 | [package.dependencies] 174 | coverage = {version = ">=5.2.1", extras = ["toml"]} 175 | pytest = ">=4.6" 176 | 177 | [package.extras] 178 | testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] 179 | 180 | [[package]] 181 | name = "toml" 182 | version = "0.10.2" 183 | description = "Python Library for Tom's Obvious, Minimal Language" 184 | category = "dev" 185 | optional = false 186 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 187 | 188 | [[package]] 189 | name = "tomli" 190 | version = "1.2.3" 191 | description = "A lil' TOML parser" 192 | category = "dev" 193 | optional = false 194 | python-versions = ">=3.6" 195 | 196 | [[package]] 197 | name = "typed-ast" 198 | version = "1.4.3" 199 | description = "a fork of Python 2 and 3 ast modules with type comment support" 200 | category = "dev" 201 | optional = false 202 | python-versions = "*" 203 | 204 | [[package]] 205 | name = "typer" 206 | version = "0.3.2" 207 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 208 | category = "main" 209 | optional = false 210 | python-versions = ">=3.6" 211 | 212 | [package.dependencies] 213 | click = ">=7.1.1,<7.2.0" 214 | 215 | [package.extras] 216 | test = ["pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.782)", "black (>=19.10b0,<20.0b0)", "isort (>=5.0.6,<6.0.0)", "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)"] 217 | all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] 218 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] 219 | doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)", "markdown-include (>=0.5.1,<0.6.0)"] 220 | 221 | [[package]] 222 | name = "typing-extensions" 223 | version = "3.10.0.0" 224 | description = "Backported and Experimental Type Hints for Python 3.5+" 225 | category = "main" 226 | optional = false 227 | python-versions = "*" 228 | 229 | [[package]] 230 | name = "zipp" 231 | version = "3.5.0" 232 | description = "Backport of pathlib-compatible object wrapper for zip files" 233 | category = "dev" 234 | optional = false 235 | python-versions = ">=3.6" 236 | 237 | [package.extras] 238 | docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] 239 | 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"] 240 | 241 | [metadata] 242 | lock-version = "1.1" 243 | python-versions = "^3.6" 244 | content-hash = "d96c62699eeb2527e39960b31b8bafb1d8a4dee6cdb1a3cea14b5074354812a5" 245 | 246 | [metadata.files] 247 | atomicwrites = [ 248 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 249 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 250 | ] 251 | attrs = [ 252 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 253 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 254 | ] 255 | click = [ 256 | {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, 257 | {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, 258 | ] 259 | colorama = [ 260 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 261 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 262 | ] 263 | coverage = [ 264 | {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, 265 | {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, 266 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, 267 | {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, 268 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, 269 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, 270 | {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, 271 | {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, 272 | {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, 273 | {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, 274 | {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, 275 | {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, 276 | {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, 277 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, 278 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, 279 | {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, 280 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, 281 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, 282 | {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, 283 | {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, 284 | {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, 285 | {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, 286 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, 287 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, 288 | {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, 289 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, 290 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, 291 | {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, 292 | {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, 293 | {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, 294 | {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, 295 | {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, 296 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, 297 | {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, 298 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, 299 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, 300 | {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, 301 | {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, 302 | {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, 303 | {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, 304 | {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, 305 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, 306 | {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, 307 | {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, 308 | {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, 309 | {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, 310 | {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, 311 | ] 312 | importlib-metadata = [ 313 | {file = "importlib_metadata-4.6.1-py3-none-any.whl", hash = "sha256:9f55f560e116f8643ecf2922d9cd3e1c7e8d52e683178fecd9d08f6aa357e11e"}, 314 | {file = "importlib_metadata-4.6.1.tar.gz", hash = "sha256:079ada16b7fc30dfbb5d13399a5113110dab1aa7c2bc62f66af75f0b717c8cac"}, 315 | ] 316 | iniconfig = [ 317 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 318 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 319 | ] 320 | mypy = [ 321 | {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, 322 | {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, 323 | {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, 324 | {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, 325 | {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, 326 | {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, 327 | {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, 328 | {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, 329 | {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, 330 | {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, 331 | {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, 332 | {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, 333 | {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, 334 | {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, 335 | {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, 336 | {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, 337 | {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, 338 | {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, 339 | {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, 340 | {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, 341 | {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, 342 | {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, 343 | ] 344 | mypy-extensions = [ 345 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 346 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 347 | ] 348 | packaging = [ 349 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, 350 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, 351 | ] 352 | pluggy = [ 353 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 354 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 355 | ] 356 | py = [ 357 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 358 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 359 | ] 360 | pyparsing = [ 361 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 362 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 363 | ] 364 | pytest = [ 365 | {file = "pytest-6.2.5-py3-none-any.whl", hash = "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134"}, 366 | {file = "pytest-6.2.5.tar.gz", hash = "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89"}, 367 | ] 368 | pytest-cov = [ 369 | {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, 370 | {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, 371 | ] 372 | toml = [ 373 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 374 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 375 | ] 376 | tomli = [ 377 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 378 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 379 | ] 380 | typed-ast = [ 381 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, 382 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, 383 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, 384 | {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, 385 | {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, 386 | {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, 387 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, 388 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, 389 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, 390 | {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, 391 | {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, 392 | {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, 393 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, 394 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, 395 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, 396 | {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, 397 | {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, 398 | {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, 399 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, 400 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, 401 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, 402 | {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, 403 | {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, 404 | {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, 405 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, 406 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, 407 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, 408 | {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, 409 | {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, 410 | {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, 411 | ] 412 | typer = [ 413 | {file = "typer-0.3.2-py3-none-any.whl", hash = "sha256:ba58b920ce851b12a2d790143009fa00ac1d05b3ff3257061ff69dbdfc3d161b"}, 414 | {file = "typer-0.3.2.tar.gz", hash = "sha256:5455d750122cff96745b0dec87368f56d023725a7ebc9d2e54dd23dc86816303"}, 415 | ] 416 | typing-extensions = [ 417 | {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, 418 | {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, 419 | {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, 420 | ] 421 | zipp = [ 422 | {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, 423 | {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, 424 | ] 425 | --------------------------------------------------------------------------------