├── src └── auto_optional │ ├── py.typed │ ├── __init__.py │ ├── data_structures.py │ ├── main.py │ ├── cst_helper.py │ ├── file_handling.py │ └── code_processing.py ├── docs ├── CNAME ├── assets │ └── images │ │ ├── favicon.png │ │ ├── og-image.png │ │ ├── social-square.png │ │ ├── logo-shape-white.svg │ │ └── logo-with-text.svg ├── overrides │ └── main.html ├── stylesheets │ └── extra-style.css └── index.md ├── mypy.ini ├── .gitignore ├── .flake8 ├── .pre-commit-hooks.yaml ├── tasks.py ├── mkdocs.yml ├── LICENSE ├── pyproject.toml ├── .github └── workflows │ ├── cd.yml │ └── ci.yml ├── README.md ├── test ├── test_all.py └── simple-tests.yaml └── poetry.lock /src/auto_optional/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/auto_optional/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | auto-optional.daanluttik.nl 2 | -------------------------------------------------------------------------------- /mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | mypy_path = src, test 3 | python_version = 3.8 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .venv/ 2 | .pytest_cache/ 3 | __pycache__/ 4 | .idea/ 5 | dist/ 6 | /.coverage 7 | 8 | -------------------------------------------------------------------------------- /docs/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luttik/auto-optional/HEAD/docs/assets/images/favicon.png -------------------------------------------------------------------------------- /docs/assets/images/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luttik/auto-optional/HEAD/docs/assets/images/og-image.png -------------------------------------------------------------------------------- /docs/assets/images/social-square.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Luttik/auto-optional/HEAD/docs/assets/images/social-square.png -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = ANN101, W503 3 | max-line-length = 88 4 | exclude = .venv, __pycache__, .py_cache, . ipynb_checkpoints, auto_optional.egg-info, tasks.py 5 | -------------------------------------------------------------------------------- /.pre-commit-hooks.yaml: -------------------------------------------------------------------------------- 1 | - id: auto-optional 2 | name: auto-optional 3 | description: Adds the Optional type-hint to arguments where the default value is None 4 | entry: auto-optional 5 | require_serial: True 6 | language: python 7 | types: [python] 8 | -------------------------------------------------------------------------------- /src/auto_optional/data_structures.py: -------------------------------------------------------------------------------- 1 | from typing import List, Union 2 | 3 | import libcst as cst 4 | 5 | 6 | class BestImportStatementFirstList(List[Union[cst.Name, cst.Attribute]]): 7 | def append(self, x: Union[cst.Name, cst.Attribute]) -> None: 8 | if self and isinstance(x, cst.Name) and isinstance(self[0], cst.Attribute): 9 | super().insert(0, x) 10 | 11 | return super().append(x) 12 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from invoke import task 2 | 3 | 4 | def simple_task(name: str, command: str): 5 | def caller(c): 6 | c.run(f"echo running {name}") 7 | c.run(command) 8 | 9 | return task(caller, name=name) 10 | 11 | 12 | black = simple_task("black", "black .") 13 | isort = simple_task("isort", "isort .") 14 | format = simple_task("format", "inv black isort") 15 | 16 | flake8 = simple_task("flake8", "flake8") 17 | mypy = simple_task("mypy", "mypy src test --namespace-packages") 18 | lint = simple_task("lint", "inv flake8 mypy") 19 | test = simple_task("test", "pytest --cov") 20 | -------------------------------------------------------------------------------- /src/auto_optional/main.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from typing import List 3 | 4 | import typer 5 | 6 | from auto_optional.file_handling import convert_path 7 | 8 | app = typer.Typer() 9 | 10 | 11 | @app.command() 12 | def main(path: List[Path] = typer.Argument(None)) -> None: 13 | if not path: 14 | path = [Path(".")] 15 | converted_files = sum(convert_path(p) for p in path) 16 | if converted_files: 17 | typer.echo(f"{converted_files} were fixed.") 18 | else: 19 | typer.echo("No issues were found.") 20 | 21 | 22 | if __name__ == "__main__": 23 | typer.run(main) 24 | -------------------------------------------------------------------------------- /src/auto_optional/cst_helper.py: -------------------------------------------------------------------------------- 1 | from typing import List, Optional, Type 2 | 3 | import libcst as cst 4 | 5 | 6 | def unwrap_commutative_binary_operator( 7 | operation_node: cst.BinaryOperation, 8 | operator: Optional[Type[cst.BaseBinaryOp]] = None, 9 | ) -> List[cst.BaseExpression]: 10 | # Created a new variable to satisfy mypy 11 | operator_not_null = operator or type(operation_node.operator) 12 | 13 | def unwrap_or_return(node: cst.BaseExpression) -> List[cst.BaseExpression]: 14 | if isinstance(node, cst.BinaryOperation) and isinstance( 15 | node.operator, operator_not_null 16 | ): 17 | return unwrap_commutative_binary_operator(node, operator_not_null) 18 | return [node] 19 | 20 | return [ 21 | *unwrap_or_return(operation_node.left), 22 | *unwrap_or_return(operation_node.right), 23 | ] 24 | -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block site_meta %} 4 | {{ super() }} 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% endblock %} -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: auto-optional 2 | site_url: https://auto-optional.daanluttik.nl/ 3 | theme: 4 | name: material 5 | icon: 6 | repo: fontawesome/brands/github 7 | font: 8 | text: inter 9 | code: JetBrains+Mono 10 | favicon: assets/images/favicon.png 11 | logo: assets/images/logo-shape-white.svg 12 | palette: 13 | - media: "(prefers-color-scheme: light)" 14 | scheme: default 15 | toggle: 16 | icon: material/weather-night 17 | name: Switch to dark mode 18 | - media: "(prefers-color-scheme: dark)" 19 | scheme: slate 20 | toggle: 21 | icon: material/weather-sunny 22 | name: Switch to light mode 23 | custom_dir: docs/overrides 24 | features: 25 | - header.autohide 26 | 27 | extra_css: 28 | - stylesheets/extra-style.css 29 | 30 | markdown_extensions: 31 | - meta 32 | - pymdownx.highlight 33 | - pymdownx.superfences 34 | - admonition 35 | - pymdownx.superfences 36 | - toc: 37 | permalink: true 38 | 39 | 40 | repo_name: luttik/auto-optional 41 | repo_url: https://github.com/luttik/auto-optional 42 | edit_uri: edit/main/docs/ 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Daan Luttik 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 | -------------------------------------------------------------------------------- /src/auto_optional/file_handling.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | import libcst as cst 4 | 5 | from auto_optional.code_processing import AutoOptionalTransformer 6 | 7 | 8 | def convert_path(path: Path) -> int: 9 | """ 10 | Converts all files in the Path. 11 | 12 | :return: The amount of changed files. 13 | """ 14 | changed_files = 0 15 | 16 | files = [] 17 | 18 | if path.is_file(): 19 | files.append(path) 20 | else: 21 | files.extend(path.glob("**/*.py")) 22 | 23 | for file_path in files: 24 | print(f"Reading file: {file_path}") 25 | with open(file_path, "r", encoding="utf-8") as file: 26 | old = file.read() 27 | new = convert_file(old) 28 | 29 | with open(file_path, "w", encoding="utf-8") as file: 30 | file.write(new) 31 | if old != new: 32 | changed_files += 1 33 | 34 | return changed_files 35 | 36 | 37 | def convert_file(py_source: str) -> str: 38 | source_tree = cst.metadata.MetadataWrapper(cst.parse_module(py_source)) 39 | modified_tree = source_tree.visit(AutoOptionalTransformer()) 40 | return modified_tree.code 41 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "auto-optional" 3 | version = "0.1.0" 4 | description = "Adds the Optional type-hint to arguments where the default value is None" 5 | authors = ["Luttik "] 6 | readme = "README.md" 7 | repository = "https://github.com/Luttik/auto-optional" 8 | homepage = "https://auto-optional.daanluttik.nl" 9 | documentation = "https://auto-optional.daanluttik.nl" 10 | 11 | [tool.poetry.scripts] 12 | auto-optional = "auto_optional.main:app" 13 | 14 | [tool.poetry.dependencies] 15 | python = ">3.7, <4.0" 16 | libcst = "^0.3.20" 17 | typer = "^0.4.0" 18 | 19 | [tool.poetry.dev-dependencies] 20 | pytest = "^6.2.4" 21 | black = "^21.8b0" 22 | isort = "^5.9.3" 23 | invoke = "^1.6.0" 24 | mypy = "^0.910" 25 | flake8 = "^3.9.2" 26 | flake8-annotations = "^2.6.2" 27 | flake8-black = "^0.2.3" 28 | flake8-isort = "^4.0.0" 29 | pytest-cov = "^2.12.1" 30 | codecov = "^2.1.12" 31 | mkdocs = "^1.2.3" 32 | mkdocs-material = "^7.2.6" 33 | PyYAML = "^5.4.1" 34 | pydantic = "^1.8.2" 35 | types-PyYAML = "^5.4.10" 36 | 37 | [tool.coverage.paths] 38 | source = ["src", "*/site-packages"] 39 | 40 | [tool.coverage.run] 41 | branch = true 42 | source = ["auto_optional"] 43 | 44 | [build-system] 45 | requires = ["poetry-core>=1.0.0"] 46 | build-backend = "poetry.core.masonry.api" 47 | -------------------------------------------------------------------------------- /docs/assets/images/logo-shape-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/workflows/cd.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run test and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: CD 5 | 6 | on: 7 | push: 8 | tags: 9 | 'v*' 10 | 11 | jobs: 12 | build-python: 13 | runs-on: ubuntu-latest 14 | strategy: 15 | matrix: 16 | python-version: [3.8] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - id: get_version 21 | name: get version from tag 22 | uses: battila7/get-version-action@v2 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v1 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | - name: Install python dependencies 28 | run: | 29 | python -m pip install --upgrade pip 30 | pip install poetry 31 | poetry config virtualenvs.create false 32 | poetry install 33 | - name: Lint with flake8 34 | run: | 35 | inv lint 36 | - name: Test with pytest 37 | run: | 38 | inv test 39 | - name: Publish to pypi 40 | run: | 41 | poetry version ${{ steps.get_version.outputs.version-without-v }} 42 | poetry publish --build --username "__token__" --password "${{ secrets.PYPI_TOKEN }}" 43 | - name: Upload Python resources to release 44 | uses: svenstaro/upload-release-action@v2 45 | with: 46 | repo_token: ${{ secrets.GITHUB_TOKEN }} 47 | file: dist/* 48 | tag: ${{ github.ref }} 49 | file_glob: true 50 | - name: Update docs 51 | run: | 52 | mkdocs gh-deploy --force 53 | -------------------------------------------------------------------------------- /docs/stylesheets/extra-style.css: -------------------------------------------------------------------------------- 1 | :root [data-md-color-scheme="default"] { 2 | --md-primary-fg-color: #fff; 3 | --md-default-bg-color: #fff; 4 | --md-code-fg-color: #000; 5 | --md-primary-bg-color: #000; 6 | --md-accent-fg-color: #07a; 7 | --md-typeset-color: #000; 8 | --md-typeset-a-color: #07a; 9 | --md-inline-code-fg-color: #dd4a68; 10 | } 11 | 12 | [data-md-color-scheme="slate"] { 13 | --md-primary-fg-color: #202124; 14 | --md-default-bg-color: #202124; 15 | --md-code-fg-color: #fff; 16 | --md-typeset-color: #fff; 17 | --md-primary-bg-color: #fff; 18 | --md-accent-fg-color: #3ac5ff; 19 | --md-typeset-a-color: #3ac5ff; 20 | --md-code-bg-color: #151517; 21 | --md-inline-code-fg-color: #dd4a68; 22 | } 23 | 24 | svg { 25 | shape-rendering: geometricPrecision; 26 | } 27 | 28 | 29 | .md-typeset code { 30 | color: var(--md-inline-code-fg-color); 31 | border-radius: 6pt; 32 | padding: 4pt 6pt; 33 | font-size: .8em; 34 | } 35 | 36 | .md-typeset blockquote { 37 | color: var(--md-typeset-color); 38 | border-left-color: var(--md-accent-fg-color); 39 | } 40 | 41 | .md-footer, .md-footer-copyright { 42 | display: none; 43 | } 44 | 45 | .md-typeset { 46 | line-height: 2em; 47 | } 48 | 49 | b, strong { 50 | font-weight: 600; 51 | } 52 | 53 | .md-header { 54 | box-shadow: 0 0.2rem 0.4rem rgba(0, 0, 0, 0.1) !important; 55 | } 56 | 57 | body[data-md-color-scheme="default"] .md-header__button.md-logo img { 58 | filter: invert(1); 59 | } 60 | 61 | [data-md-color-scheme="default"] .md-header img { 62 | rgba(var(--md-primary-bg-color), .7) 63 | } 64 | 65 | .md-header .md-search__form { 66 | background-color: rgba(0, 0, 0, .05); 67 | } 68 | 69 | .md-header .md-search .md-search__input, .md-header .md-search .md-search__icon { 70 | color: var(--md-code-fg-color); 71 | } 72 | 73 | .md-header .md-search .md-search__input::placeholder { 74 | color: rgba(var(--md-typeset-color), .4) 75 | } 76 | 77 | 78 | h1, 79 | h2, 80 | h3, 81 | h4, 82 | h5, 83 | h6 { 84 | line-height: 1.5em; 85 | margin-block-start: 1.75em; 86 | margin-block-end: 0.5em; 87 | color: var(--md-typeset-color) !important; 88 | font-weight: bold !important; 89 | } 90 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This workflow will install Python dependencies, run test and lint with a variety of Python versions 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions 3 | 4 | name: CI 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | 13 | jobs: 14 | lint: 15 | runs-on: ubuntu-latest 16 | strategy: 17 | matrix: 18 | python-version: [3.9] 19 | 20 | steps: 21 | - uses: actions/checkout@v2 22 | - name: Set up Python ${{ matrix.python-version }} 23 | uses: actions/setup-python@v1 24 | with: 25 | python-version: ${{ matrix.python-version }} 26 | - name: Install dependencies 27 | run: | 28 | python -m pip install --upgrade pip 29 | pip install poetry 30 | poetry config virtualenvs.create false 31 | poetry install 32 | - name: Lint 33 | run: | 34 | inv lint 35 | 36 | test: 37 | runs-on: ubuntu-latest 38 | strategy: 39 | matrix: 40 | python-version: [3.7, 3.8, 3.9] 41 | 42 | steps: 43 | - uses: actions/checkout@v2 44 | - name: Set up Python ${{ matrix.python-version }} 45 | uses: actions/setup-python@v1 46 | with: 47 | python-version: ${{ matrix.python-version }} 48 | - name: Install dependencies 49 | run: | 50 | python -m pip install --upgrade pip 51 | pip install poetry 52 | poetry config virtualenvs.create false 53 | poetry install 54 | - name: Test 55 | run: | 56 | inv test 57 | 58 | code-cov: 59 | runs-on: ubuntu-latest 60 | strategy: 61 | matrix: 62 | python-version: [3.9] 63 | 64 | steps: 65 | - uses: actions/checkout@v2 66 | - name: Set up Python ${{ matrix.python-version }} 67 | uses: actions/setup-python@v1 68 | with: 69 | python-version: ${{ matrix.python-version }} 70 | - name: Install dependencies 71 | run: | 72 | python -m pip install --upgrade pip 73 | pip install poetry 74 | poetry config virtualenvs.create false 75 | poetry install 76 | - name: upload codecov data 77 | run: | 78 | pytest --cov --cov-fail-under=0 --cov-report xml 79 | codecov 80 | env: 81 | CODECOV_TOKEN: ${{secrets.CODECOV_TOKEN}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # auto-optional 2 | Logo 3 | 4 | 5 |

6 | auto-optional: adds the Optional type-hint to arguments where the default value is None 7 |

8 | 9 |

10 | 11 | actions batch 12 | 13 | 14 | pypi 15 | 16 | 17 | python versions 18 | 19 | 20 | codecov 21 | 22 | 23 | License: MIT 24 | 25 | 26 | Code style: black 27 | 28 |

29 | 30 | --- 31 | 32 | **Documentation**: [auto-optional.daanluttik.nl](https://auto-optional.daanluttik.nl) 33 | 34 | **Source Code**: [github.com/luttik/auto-optional](https://github.com/Luttik/auto-optional) 35 | 36 | --- 37 | 38 | ## What does auto-optional do 39 | The basic purpose of auto-optional is ensuring that whenever a default argument is `None` the type annotation is Optional. 40 | 41 | For example: 42 | ```py 43 | def foo(bar: str = None): 44 | ... 45 | ``` 46 | 47 | Would turn into 48 | 49 | ```py 50 | from typing import Optional 51 | def foo(bar: Optional[str] = None): 52 | ... 53 | ``` 54 | 55 | ## Why would you want this 56 | 57 | - Easily modify external libraries that didn't pay attention 58 | to proper use of optional to improve mypy lintingf. 59 | - Force consistency in your own code-base: 60 | Enforcing that `None` parameter implies an `Optional` type. 61 | - Explicit is better than implicit — [pep 20](https://www.python.org/dev/peps/pep-0020/) 62 | 63 | ## In the media: 64 | auto-optional was covered on 65 | [PythonBytes #251](https://pythonbytes.fm/episodes/show/251/a-95-complete-episode-wait-for-it) 66 | 67 | > I love these little tools that you can run against your code that will just reformat them to be better. 68 | > 69 | > — Michael Kennedy 70 | 71 | ## Install 72 | Install with `pip install auto-optional`. 73 | 74 | ## Run 75 | After installing you can run auto-optional using `auto-optional [paths...]` 76 | (if no path is provided it'll process the current working directory). 77 | 78 | ## pre-commit 79 | 80 | You can run auto-optional via [pre-commit](https://pre-commit.com/). 81 | Add the following text to your repositories `.pre-commit-config.yaml`: 82 | 83 | ```yaml 84 | repos: 85 | - repo: https://github.com/luttik/auto-optional 86 | rev: v0.3.1 # The version of auto-optional to use 87 | hooks: 88 | - id: auto-optional 89 | ``` 90 | 91 | ## Things of note 92 | 93 | ### Things that are handled well 94 | 95 | - The alternatives to `Optional` are supported, that means both; 96 | - `Union[X, None]` 97 | - `x | None` (allowed since python 3.10+). 98 | - Existing imports are reused. 99 | - `import as` and `from typing import ...` statements are properly handled. 100 | 101 | ### Things that need improvement 102 | For all these points you can leave a thumbs-up if you want it. Also, I welcome pull-requests for these issues. 103 | 104 | - There is no exclude (for file patterns) option yet [[#2]](https://github.com/Luttik/auto-optional/issues/2) 105 | - There is no ignore (for code lines) option yet [[#3]](https://github.com/Luttik/auto-optional/issues/3) 106 | - Code is aways read and written as `UTF-8` (which is accurate most of the time). [[#4]](https://github.com/Luttik/auto-optional/issues/4) 107 | - There is no `diff` or `check` command yet for a dry-run or linting. [[#5]](https://github.com/Luttik/auto-optional/issues/5) 108 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | hide: 3 | - navigation 4 | --- 5 | 6 | # auto-optional 7 | Logo 8 | 9 | 10 |

11 | auto-optional: adds the Optional type-hint to arguments where the default value is None 12 |

13 | 14 |

15 | 16 | actions batch 17 | 18 | 19 | pypi 20 | 21 | 22 | python versions 23 | 24 | 25 | codecov 26 | 27 | 28 | License: MIT 29 | 30 | 31 | Code style: black 32 | 33 |

34 | 35 | --- 36 | 37 | **Documentation**: [auto-optional.daanluttik.nl](https://auto-optional.daanluttik.nl) 38 | 39 | **Source Code**: [github.com/luttik/auto-optional](https://github.com/Luttik/auto-optional) 40 | 41 | --- 42 | 43 | ## What does auto-optional do 44 | The basic purpose of auto-optional is ensuring that whenever a default argument is `None` the type annotation is Optional. 45 | 46 | For example: 47 | ```py 48 | def foo(bar: str = None): 49 | ... 50 | ``` 51 | 52 | Would turn into 53 | 54 | ```py 55 | from typing import Optional 56 | def foo(bar: Optional[str] = None): 57 | ... 58 | ``` 59 | 60 | ## Why would you want this 61 | 62 | - Easily modify external libraries that didn't pay attention 63 | to proper use of optional to improve mypy linting. 64 | - Force consistency in your own code-base: 65 | Enforcing that `None` parameter implies an `Optional` type. 66 | - Explicit is better than implicit — [pep 20](https://www.python.org/dev/peps/pep-0020/) 67 | 68 | ## In the media: 69 | auto-optional was covered on 70 | [PythonBytes #251](https://pythonbytes.fm/episodes/show/251/a-95-complete-episode-wait-for-it) 71 | 72 | > I love these little tools that you can run against your code that will just reformat them to be better. 73 | > 74 | > — Michael Kennedy 75 | 76 | ## Install 77 | Install with `pip install auto-optional`. 78 | 79 | ## Run 80 | After installing you can run auto-optional using`auto-optional [paths...]` 81 | (if no path is provided it'll process the current working directory). 82 | 83 | ## pre-commit 84 | 85 | You can run auto-optional via [pre-commit](https://pre-commit.com/). 86 | Add the following text to your repositories `.pre-commit-config.yaml`: 87 | 88 | ```yaml 89 | repos: 90 | - repo: https://github.com/luttik/auto-optional 91 | rev: v0.2.0 # The version of auto-optional to use 92 | hooks: 93 | - id: auto-optional 94 | ``` 95 | 96 | ## Things of note 97 | 98 | ### Things that are handled well 99 | 100 | - The alternatives to `Optional` are supported, that means both; 101 | - `Union[X, None]` 102 | - `x | None` (allowed since python 3.10+). 103 | - Existing imports are reused. 104 | - `import as` and `from typing import ...` statements are properly handled. 105 | 106 | ### Things that need improvement 107 | For all these points you can leave a thumbs-up if you want it. Also, I welcome pull-requests for these issues. 108 | 109 | - There is no exclude (for file patterns) option yet [[#2]](https://github.com/Luttik/auto-optional/issues/2) 110 | - There is no ignore (for code lines) option yet [[#3]](https://github.com/Luttik/auto-optional/issues/3) 111 | - Code is aways read and written as `UTF-8` (which is accurate most of the time). [[#4]](https://github.com/Luttik/auto-optional/issues/4) 112 | - There is no `diff` or `check` command yet for a dry-run or linting. [[#5]](https://github.com/Luttik/auto-optional/issues/5) 113 | -------------------------------------------------------------------------------- /test/test_all.py: -------------------------------------------------------------------------------- 1 | from abc import ABC 2 | from pathlib import Path 3 | from typing import List, Union 4 | 5 | import py 6 | import pytest 7 | from pydantic import BaseModel 8 | from typer.testing import CliRunner 9 | from yaml import load 10 | 11 | from auto_optional.file_handling import convert_file 12 | from auto_optional.main import app 13 | 14 | try: 15 | from yaml import CLoader as YamlLoader 16 | except ImportError: 17 | from yaml import YamlLoader # type: ignore 18 | 19 | 20 | runner = CliRunner() 21 | 22 | 23 | class BaseTestConfig(BaseModel, ABC): 24 | name: str 25 | 26 | 27 | class BeforeAndAfterTest(BaseTestConfig): 28 | before: str 29 | after: str 30 | 31 | 32 | class UnchangedTest(BaseTestConfig): 33 | unchanged: str 34 | 35 | 36 | class SimpleTestConfig(BaseModel): 37 | tests: List[Union[BeforeAndAfterTest, UnchangedTest]] 38 | 39 | 40 | def get_singe_file_tests() -> List: 41 | with (Path(__file__).parent / "simple-tests.yaml").open() as file: 42 | return SimpleTestConfig( 43 | **load( 44 | file, 45 | Loader=YamlLoader, 46 | ) 47 | ).tests 48 | 49 | 50 | SINGLE_FILE_TESTS = get_singe_file_tests() 51 | 52 | 53 | def prepare_file( 54 | test_config: Union[BeforeAndAfterTest, UnchangedTest], tmpdir: Path 55 | ) -> Path: 56 | if isinstance(test_config, BeforeAndAfterTest): 57 | code = test_config.before 58 | elif isinstance(test_config, UnchangedTest): 59 | code = test_config.unchanged 60 | else: 61 | raise NotImplementedError( 62 | f"tests of type {type(test_config)} are not yet supported" 63 | ) 64 | 65 | code_path = tmpdir / f"{hash(code)}.py" 66 | code_path.write_text(code) 67 | return code_path 68 | 69 | 70 | def assert_code_processed_correctly( 71 | test_config: Union[BeforeAndAfterTest, UnchangedTest], 72 | path: Path, 73 | ) -> None: 74 | if isinstance(test_config, BeforeAndAfterTest): 75 | code = test_config.after 76 | elif isinstance(test_config, UnchangedTest): 77 | code = test_config.unchanged 78 | else: 79 | raise NotImplementedError( 80 | f"tests of type {type(test_config)} are not yet supported" 81 | ) 82 | 83 | assert path.read_text() == code 84 | 85 | 86 | @pytest.mark.parametrize( 87 | "test_config", 88 | SINGLE_FILE_TESTS, 89 | ids=[test.name for test in SINGLE_FILE_TESTS], 90 | ) 91 | def test_single_files_from_yaml( 92 | test_config: Union[BeforeAndAfterTest, UnchangedTest] 93 | ) -> None: 94 | if isinstance(test_config, BeforeAndAfterTest): 95 | assert convert_file(test_config.before) == test_config.after 96 | elif isinstance(test_config, UnchangedTest): 97 | assert convert_file(test_config.unchanged) == test_config.unchanged 98 | else: 99 | raise NotImplementedError( 100 | f"tests of type {type(test_config)} are not yet supported" 101 | ) 102 | 103 | 104 | @pytest.mark.parametrize( 105 | "test_config", 106 | SINGLE_FILE_TESTS, 107 | ids=[test.name for test in SINGLE_FILE_TESTS], 108 | ) 109 | def test_cli_single_files_from_yaml( 110 | test_config: Union[BeforeAndAfterTest, UnchangedTest], tmpdir: py.path.local 111 | ) -> None: 112 | single_code_file_path = prepare_file(test_config, Path(tmpdir)) 113 | runner.invoke(app, [str(single_code_file_path)]) 114 | assert_code_processed_correctly(test_config, single_code_file_path) 115 | 116 | 117 | @pytest.mark.parametrize( 118 | "test_config", 119 | SINGLE_FILE_TESTS, 120 | ids=[test.name for test in SINGLE_FILE_TESTS], 121 | ) 122 | def test_cli_no_file_should_use_current_dir( 123 | test_config: Union[BeforeAndAfterTest, UnchangedTest], tmpdir: py.path.local 124 | ) -> None: 125 | tmpdir.chdir() 126 | single_code_file_path = prepare_file(test_config, Path(tmpdir)) 127 | runner.invoke(app) 128 | assert_code_processed_correctly(test_config, single_code_file_path) 129 | 130 | 131 | def test_cli_multi_file(tmpdir: py.path.local) -> None: 132 | test_with_paths = [ 133 | (test_config, prepare_file(test_config, Path(tmpdir))) 134 | for test_config in SINGLE_FILE_TESTS 135 | ] 136 | 137 | runner.invoke(app, [str(path) for _, path in test_with_paths]) 138 | 139 | for test_config, path in test_with_paths: 140 | assert_code_processed_correctly(test_config, path) 141 | -------------------------------------------------------------------------------- /test/simple-tests.yaml: -------------------------------------------------------------------------------- 1 | tests: 2 | - name: test_simple_change 3 | before: | 4 | def bla(foo: str = None): 5 | ... 6 | after: | 7 | from typing import Optional 8 | def bla(foo: Optional[str] = None): 9 | ... 10 | 11 | - name: test_simple_change_already_imported 12 | before: | 13 | from typing import Optional 14 | def bla(foo: 15 | str = None): 16 | ... 17 | after: | 18 | from typing import Optional 19 | def bla(foo: Optional[str] = None): 20 | ... 21 | 22 | - name: test_simple_change_star_import 23 | before: | 24 | from typing import * 25 | def bla(foo: 26 | str = None): 27 | ... 28 | after: | 29 | from typing import * 30 | def bla(foo: Optional[str] = None): 31 | ... 32 | 33 | - name: test_simple_change_already_imported_optional_as 34 | before: | 35 | from typing import Optional as O 36 | def bla(foo: str = None): 37 | ... 38 | after: | 39 | from typing import Optional as O 40 | def bla(foo: O[str] = None): 41 | ... 42 | 43 | - name: test_nested_change 44 | before: | 45 | def bla(foo: Tuple[str] = None): 46 | ... 47 | after: | 48 | from typing import Optional 49 | def bla(foo: Optional[Tuple[str]] = None): 50 | ... 51 | 52 | - name: test_not_optional 53 | unchanged: | 54 | def bla(foo: 55 | str = 'a'): 56 | ... 57 | 58 | - name: test_already_optional 59 | unchanged: | 60 | from typing import Optional 61 | def bla(foo: 62 | Optional[str] = None): 63 | ... 64 | 65 | - name: test_legal_no_type 66 | unchanged: | 67 | class Bloep: 68 | def bla(self): 69 | ... 70 | 71 | - name: test_nochange_typing_import 72 | unchanged: | 73 | from typing import Optional 74 | def bla(foo: 75 | Optional[str] = None): 76 | ... 77 | 78 | - name: test_nochange_renamed_typing 79 | unchanged: | 80 | import typing 81 | import typing as t 82 | def bla(foo: t.Optional[str] = None): 83 | ... 84 | 85 | - name: test_prioritise_from_import 86 | before: | 87 | import typing 88 | from typing import Optional 89 | def bla(foo: str = None): 90 | ... 91 | after: | 92 | import typing 93 | from typing import Optional 94 | def bla(foo: Optional[str] = None): 95 | ... 96 | 97 | - name: test_with_module_docstring 98 | before: | 99 | """Something""" 100 | def f(s: str = None): 101 | ... 102 | after: | 103 | """Something""" 104 | from typing import Optional 105 | def f(s: Optional[str] = None): 106 | ... 107 | 108 | - name: test_with_future_import 109 | before: | 110 | from __future__ import annotations 111 | def f(s: str = None): 112 | ... 113 | after: | 114 | from __future__ import annotations 115 | from typing import Optional 116 | def f(s: Optional[str] = None): 117 | ... 118 | 119 | - name: test_docstring_with_future_import 120 | before: | 121 | """Something""" 122 | from __future__ import annotations 123 | def f(s: str = None): 124 | ... 125 | after: | 126 | """Something""" 127 | from __future__ import annotations 128 | from typing import Optional 129 | def f(s: Optional[str] = None): 130 | ... 131 | 132 | - name: test_nochange_typing_as_import 133 | unchanged: | 134 | import typing as t 135 | def bla(foo: t.Optional[str] = None): 136 | ... 137 | 138 | - name: test_accept_union_none_from_import 139 | unchanged: | 140 | import typing as t 141 | def bla(foo: t.Union[str, None] = None): 142 | ... 143 | 144 | - name: test_accept_union_none 145 | unchanged: | 146 | from typing import Union 147 | def bla(foo: Union[str, None] = None): 148 | ... 149 | 150 | - name: test_py10_syntax 151 | unchanged: | 152 | def bla(foo: str | None = None): 153 | ... 154 | 155 | - name: test_py10_syntax_extra_left 156 | unchanged: | 157 | def bla(foo: str | None | int = None): 158 | ... 159 | 160 | - name: test_py10_syntax_extra_right 161 | unchanged: | 162 | def bla(foo: str | int | None = None): 163 | ... 164 | -------------------------------------------------------------------------------- /docs/assets/images/logo-with-text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/auto_optional/code_processing.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | import libcst as cst 4 | import libcst.matchers as m 5 | 6 | from auto_optional.cst_helper import unwrap_commutative_binary_operator 7 | from auto_optional.data_structures import BestImportStatementFirstList 8 | 9 | 10 | class ImportStatementChecker: 11 | import_names: BestImportStatementFirstList 12 | imported_module: str 13 | imported_value: str 14 | 15 | def __init__(self, imported_module: str, imported_object: str) -> None: 16 | super().__init__() 17 | # Will be a list of all existing import statements 18 | # that references the desired type 19 | self.import_names = BestImportStatementFirstList() 20 | self.imported_module = imported_module 21 | self.imported_value = imported_object 22 | 23 | def get_import_name(self) -> Union[cst.Name, cst.Attribute]: 24 | return self.import_names[0] 25 | 26 | def check_import_from(self, node: cst.ImportFrom) -> None: 27 | assert node.module 28 | if node.module.value == self.imported_module: 29 | if isinstance(node.names, cst.ImportStar): 30 | # When ``from module import *`` is found the 31 | self.import_names.append(cst.Name(self.imported_value)) 32 | else: 33 | for import_alias in node.names: 34 | if import_alias.name.value == self.imported_value: 35 | self.import_names.append( 36 | cst.ensure_type(import_alias.asname.name, cst.Name) 37 | if import_alias.asname 38 | else import_alias.name 39 | ) 40 | 41 | def check_import(self, node: cst.Import) -> None: 42 | for import_alias in node.names: 43 | if import_alias.name.value == self.imported_module: 44 | typing_name = ( 45 | cst.ensure_type(import_alias.asname.name, cst.Name) 46 | if import_alias.asname 47 | # ensure_type to satisfy mypy 48 | # it is a Name since its value equals the module (see if statement) 49 | else cst.ensure_type(import_alias.name, cst.Name) 50 | ) 51 | 52 | self.import_names.append( 53 | cst.Attribute( 54 | value=cst.Name(typing_name.value), 55 | attr=cst.Name(self.imported_value), 56 | ) 57 | ) 58 | 59 | 60 | class AutoOptionalTransformer(cst.CSTTransformer): 61 | METADATA_DEPENDENCIES = (cst.metadata.QualifiedNameProvider,) 62 | 63 | def __init__(self) -> None: 64 | super().__init__() 65 | self.optional_import_checker = ImportStatementChecker("typing", "Optional") 66 | self.union_import_checker = ImportStatementChecker("typing", "Union") 67 | 68 | # Will be set to true if there if we find an non-optional param with None 69 | # as default i.e. ``def foo(bar: str = None)``. 70 | self.optional_import_needed = False 71 | 72 | def visit_ImportFrom_names(self, node: cst.ImportFrom) -> None: 73 | """Searches for imports of ``typing.Optional`` and stores for later use""" 74 | self.optional_import_checker.check_import_from(node) 75 | self.union_import_checker.check_import_from(node) 76 | 77 | def visit_Import_names(self, node: cst.Import) -> None: 78 | """Searches for imports of ``typing`` and stores for later use""" 79 | self.optional_import_checker.check_import(node) 80 | self.union_import_checker.check_import(node) 81 | 82 | def visit_Param(self, node: cst.Param) -> None: 83 | """ 84 | Used to detect if there are any statements that need to be changed to Optional 85 | """ 86 | if ( 87 | node.default 88 | and m.matches(node.default, m.Name("None")) 89 | and not self.optional_import_checker.import_names 90 | ): 91 | # TODO add check for the ``Optional[None]`` alternative 92 | self.optional_import_needed = True 93 | 94 | def leave_Module( 95 | self, original_node: cst.Module, updated_node: cst.Module 96 | ) -> cst.Module: 97 | """ 98 | The last call that is handled in the process of parsing and converting the code. 99 | 100 | Is only used to add the ``typing.Optional`` import statement when missing. 101 | """ 102 | if ( 103 | not self.optional_import_checker.import_names 104 | or not self.optional_import_needed 105 | ): 106 | return updated_node 107 | 108 | import_statement = self.__build_import_statement() 109 | import_statement_location = self.__find_line_for_optional_import_insertion( 110 | updated_node 111 | ) 112 | return updated_node.with_changes( 113 | body=[ 114 | *updated_node.body[:import_statement_location], 115 | import_statement, 116 | *updated_node.body[import_statement_location:], 117 | ] 118 | ) 119 | 120 | def __build_import_statement(self) -> cst.SimpleStatementLine: 121 | return cst.SimpleStatementLine( 122 | body=[ 123 | cst.ImportFrom( 124 | module=cst.Name( 125 | value="typing", 126 | ), 127 | names=[ 128 | cst.ImportAlias( 129 | name=cst.Name( 130 | value="Optional", 131 | ), 132 | ), 133 | ], 134 | ) 135 | ] 136 | ) 137 | 138 | def __find_line_for_optional_import_insertion( 139 | self, updated_node: cst.Module 140 | ) -> int: 141 | insert_at = 0 142 | for index, body_part in enumerate(updated_node.body): 143 | if ( 144 | isinstance(body_part, cst.SimpleStatementLine) 145 | and len(body_part.children) > 0 146 | and ( 147 | ( # module level docstring 148 | index == 0 149 | and m.matches(body_part.children[0], m.Expr(m.SimpleString())) 150 | ) 151 | or m.matches( # future import 152 | body_part.children[0], 153 | m.ImportFrom(module=m.Name("__future__")), 154 | ) 155 | ) 156 | ): 157 | # module docstring 158 | insert_at += 1 159 | elif index > 1: 160 | break 161 | return insert_at 162 | 163 | def __check_if__union_none(self, annotation: cst.BaseExpression) -> bool: 164 | # Return if it is a Union[..., None, ...] statement 165 | return bool( 166 | # Check if Union can even be imported 167 | self.union_import_checker.import_names 168 | # Check if it is a Union statement at all 169 | and ( 170 | isinstance(annotation, cst.Subscript) 171 | and m.matches( 172 | annotation.value, 173 | self.union_import_checker.get_import_name(), # type: ignore 174 | ) 175 | ) 176 | # Check if any of the slice items is None 177 | and any( 178 | ( 179 | isinstance(item.slice, cst.Index) 180 | and isinstance(item.slice.value, cst.Name) 181 | and item.slice.value.value == "None" 182 | ) 183 | for item in annotation.slice 184 | ) 185 | ) 186 | 187 | def __check_if__or_none(self, annotation: cst.BaseExpression) -> bool: 188 | return ( 189 | isinstance(annotation, cst.BinaryOperation) 190 | and isinstance(annotation.operator, cst.BitOr) 191 | and any( 192 | m.matches(option, m.Name(value="None")) 193 | for option in unwrap_commutative_binary_operator(annotation) 194 | ) 195 | ) 196 | 197 | def leave_Param( 198 | self, original_node: cst.Param, updated_node: cst.Param 199 | ) -> cst.Param: 200 | """ 201 | Changes Parameters of type ``x`` to ``Optional[x]`` if their default is None. 202 | """ 203 | if ( # Check if optional is required 204 | updated_node.annotation 205 | and updated_node.default 206 | and m.matches(updated_node.default, m.Name("None")) 207 | ): 208 | annotation = updated_node.annotation.annotation 209 | 210 | # TODO if ``Union[None]`` return None 211 | 212 | # Also allow Union 213 | if self.__check_if__union_none(annotation) or self.__check_if__or_none( 214 | annotation 215 | ): 216 | return updated_node 217 | 218 | if not self.optional_import_checker.import_names: 219 | self.optional_import_checker.import_names.append(cst.Name("Optional")) 220 | 221 | if not ( 222 | isinstance(annotation, cst.Subscript) 223 | and any( 224 | m.matches(annotation.value, optional_import) # type: ignore 225 | for optional_import in self.optional_import_checker.import_names 226 | ) 227 | ): 228 | return updated_node.with_changes( 229 | annotation=cst.Annotation( 230 | cst.Subscript( 231 | value=(self.optional_import_checker.get_import_name()), 232 | slice=[cst.SubscriptElement(cst.Index(annotation))], 233 | ) 234 | ) 235 | ) 236 | return updated_node 237 | -------------------------------------------------------------------------------- /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)", "furo", "hypothesis", "mypy", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "sphinx", "sphinx-notfound-page", "zope.interface"] 19 | docs = ["furo", "sphinx", "sphinx-notfound-page", "zope.interface"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six", "zope.interface"] 21 | tests-no-zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "mypy", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "six"] 22 | 23 | [[package]] 24 | name = "black" 25 | version = "21.8b0" 26 | description = "The uncompromising code formatter." 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=3.6.2" 30 | 31 | [package.dependencies] 32 | click = ">=7.1.2" 33 | mypy-extensions = ">=0.4.3" 34 | pathspec = ">=0.9.0,<1" 35 | platformdirs = ">=2" 36 | regex = ">=2020.1.8" 37 | tomli = ">=0.2.6,<2.0.0" 38 | typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\""} 39 | typing-extensions = [ 40 | {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, 41 | {version = ">=3.10.0.0,<3.10.0.1 || >3.10.0.1", markers = "python_version >= \"3.10\""}, 42 | ] 43 | 44 | [package.extras] 45 | colorama = ["colorama (>=0.4.3)"] 46 | d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] 47 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 48 | python2 = ["typed-ast (>=1.4.2)"] 49 | uvloop = ["uvloop (>=0.15.2)"] 50 | 51 | [[package]] 52 | name = "certifi" 53 | version = "2022.12.7" 54 | description = "Python package for providing Mozilla's CA Bundle." 55 | category = "dev" 56 | optional = false 57 | python-versions = ">=3.6" 58 | 59 | [[package]] 60 | name = "charset-normalizer" 61 | version = "2.0.4" 62 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 63 | category = "dev" 64 | optional = false 65 | python-versions = ">=3.5.0" 66 | 67 | [package.extras] 68 | unicode-backport = ["unicodedata2"] 69 | 70 | [[package]] 71 | name = "click" 72 | version = "8.0.1" 73 | description = "Composable command line interface toolkit" 74 | category = "main" 75 | optional = false 76 | python-versions = ">=3.6" 77 | 78 | [package.dependencies] 79 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 80 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 81 | 82 | [[package]] 83 | name = "codecov" 84 | version = "2.1.12" 85 | description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab" 86 | category = "dev" 87 | optional = false 88 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 89 | 90 | [package.dependencies] 91 | coverage = "*" 92 | requests = ">=2.7.9" 93 | 94 | [[package]] 95 | name = "colorama" 96 | version = "0.4.4" 97 | description = "Cross-platform colored terminal text." 98 | category = "main" 99 | optional = false 100 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 101 | 102 | [[package]] 103 | name = "coverage" 104 | version = "5.5" 105 | description = "Code coverage measurement for Python" 106 | category = "dev" 107 | optional = false 108 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 109 | 110 | [package.extras] 111 | toml = ["toml"] 112 | 113 | [[package]] 114 | name = "flake8" 115 | version = "3.9.2" 116 | description = "the modular source code checker: pep8 pyflakes and co" 117 | category = "dev" 118 | optional = false 119 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 120 | 121 | [package.dependencies] 122 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 123 | mccabe = ">=0.6.0,<0.7.0" 124 | pycodestyle = ">=2.7.0,<2.8.0" 125 | pyflakes = ">=2.3.0,<2.4.0" 126 | 127 | [[package]] 128 | name = "flake8-annotations" 129 | version = "2.6.2" 130 | description = "Flake8 Type Annotation Checks" 131 | category = "dev" 132 | optional = false 133 | python-versions = ">=3.6.1,<4.0.0" 134 | 135 | [package.dependencies] 136 | flake8 = ">=3.7,<4.0" 137 | typed-ast = {version = ">=1.4,<2.0", markers = "python_version < \"3.8\""} 138 | 139 | [[package]] 140 | name = "flake8-black" 141 | version = "0.2.3" 142 | description = "flake8 plugin to call black as a code style validator" 143 | category = "dev" 144 | optional = false 145 | python-versions = "*" 146 | 147 | [package.dependencies] 148 | black = "*" 149 | flake8 = ">=3.0.0" 150 | toml = "*" 151 | 152 | [[package]] 153 | name = "flake8-isort" 154 | version = "4.0.0" 155 | description = "flake8 plugin that integrates isort ." 156 | category = "dev" 157 | optional = false 158 | python-versions = "*" 159 | 160 | [package.dependencies] 161 | flake8 = ">=3.2.1,<4" 162 | isort = ">=4.3.5,<6" 163 | testfixtures = ">=6.8.0,<7" 164 | 165 | [package.extras] 166 | test = ["pytest (>=4.0.2,<6)", "toml"] 167 | 168 | [[package]] 169 | name = "ghp-import" 170 | version = "2.0.1" 171 | description = "Copy your docs directly to the gh-pages branch." 172 | category = "dev" 173 | optional = false 174 | python-versions = "*" 175 | 176 | [package.dependencies] 177 | python-dateutil = ">=2.8.1" 178 | 179 | [package.extras] 180 | dev = ["flake8", "markdown", "twine", "wheel"] 181 | 182 | [[package]] 183 | name = "idna" 184 | version = "3.2" 185 | description = "Internationalized Domain Names in Applications (IDNA)" 186 | category = "dev" 187 | optional = false 188 | python-versions = ">=3.5" 189 | 190 | [[package]] 191 | name = "importlib-metadata" 192 | version = "4.8.1" 193 | description = "Read metadata from Python packages" 194 | category = "main" 195 | optional = false 196 | python-versions = ">=3.6" 197 | 198 | [package.dependencies] 199 | typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} 200 | zipp = ">=0.5" 201 | 202 | [package.extras] 203 | docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] 204 | perf = ["ipython"] 205 | testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pep517", "pyfakefs", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy", "pytest-perf (>=0.9.2)"] 206 | 207 | [[package]] 208 | name = "iniconfig" 209 | version = "1.1.1" 210 | description = "iniconfig: brain-dead simple config-ini parsing" 211 | category = "dev" 212 | optional = false 213 | python-versions = "*" 214 | 215 | [[package]] 216 | name = "invoke" 217 | version = "1.6.0" 218 | description = "Pythonic task execution" 219 | category = "dev" 220 | optional = false 221 | python-versions = "*" 222 | 223 | [[package]] 224 | name = "isort" 225 | version = "5.9.3" 226 | description = "A Python utility / library to sort Python imports." 227 | category = "dev" 228 | optional = false 229 | python-versions = ">=3.6.1,<4.0" 230 | 231 | [package.extras] 232 | colors = ["colorama (>=0.4.3,<0.5.0)"] 233 | pipfile-deprecated-finder = ["pipreqs", "requirementslib"] 234 | plugins = ["setuptools"] 235 | requirements-deprecated-finder = ["pip-api", "pipreqs"] 236 | 237 | [[package]] 238 | name = "jinja2" 239 | version = "3.0.1" 240 | description = "A very fast and expressive template engine." 241 | category = "dev" 242 | optional = false 243 | python-versions = ">=3.6" 244 | 245 | [package.dependencies] 246 | MarkupSafe = ">=2.0" 247 | 248 | [package.extras] 249 | i18n = ["Babel (>=2.7)"] 250 | 251 | [[package]] 252 | name = "libcst" 253 | version = "0.3.23" 254 | description = "A concrete syntax tree with AST-like properties for Python 3.5, 3.6, 3.7 and 3.8 programs." 255 | category = "main" 256 | optional = false 257 | python-versions = ">=3.6" 258 | 259 | [package.dependencies] 260 | pyyaml = ">=5.2" 261 | typing-extensions = ">=3.7.4.2" 262 | typing-inspect = ">=0.4.0" 263 | 264 | [package.extras] 265 | dev = ["black (==21.10b0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=3.7.8)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jupyter (>=1.0.0)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.3)", "setuptools-scm (>=6.0.1)", "sphinx-rtd-theme (>=0.4.3)", "tox (>=3.18.1)", "ufmt (==1.2)", "usort (==0.6.3)"] 266 | 267 | [[package]] 268 | name = "markdown" 269 | version = "3.3.4" 270 | description = "Python implementation of Markdown." 271 | category = "dev" 272 | optional = false 273 | python-versions = ">=3.6" 274 | 275 | [package.dependencies] 276 | importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} 277 | 278 | [package.extras] 279 | testing = ["coverage", "pyyaml"] 280 | 281 | [[package]] 282 | name = "markupsafe" 283 | version = "2.0.1" 284 | description = "Safely add untrusted strings to HTML/XML markup." 285 | category = "dev" 286 | optional = false 287 | python-versions = ">=3.6" 288 | 289 | [[package]] 290 | name = "mccabe" 291 | version = "0.6.1" 292 | description = "McCabe checker, plugin for flake8" 293 | category = "dev" 294 | optional = false 295 | python-versions = "*" 296 | 297 | [[package]] 298 | name = "mergedeep" 299 | version = "1.3.4" 300 | description = "A deep merge function for 🐍." 301 | category = "dev" 302 | optional = false 303 | python-versions = ">=3.6" 304 | 305 | [[package]] 306 | name = "mkdocs" 307 | version = "1.2.3" 308 | description = "Project documentation with Markdown." 309 | category = "dev" 310 | optional = false 311 | python-versions = ">=3.6" 312 | 313 | [package.dependencies] 314 | click = ">=3.3" 315 | ghp-import = ">=1.0" 316 | importlib-metadata = ">=3.10" 317 | Jinja2 = ">=2.10.1" 318 | Markdown = ">=3.2.1" 319 | mergedeep = ">=1.3.4" 320 | packaging = ">=20.5" 321 | PyYAML = ">=3.10" 322 | pyyaml-env-tag = ">=0.1" 323 | watchdog = ">=2.0" 324 | 325 | [package.extras] 326 | i18n = ["babel (>=2.9.0)"] 327 | 328 | [[package]] 329 | name = "mkdocs-material" 330 | version = "7.2.6" 331 | description = "A Material Design theme for MkDocs" 332 | category = "dev" 333 | optional = false 334 | python-versions = "*" 335 | 336 | [package.dependencies] 337 | markdown = ">=3.2" 338 | mkdocs = ">=1.2.2" 339 | mkdocs-material-extensions = ">=1.0" 340 | Pygments = ">=2.4" 341 | pymdown-extensions = ">=7.0" 342 | 343 | [[package]] 344 | name = "mkdocs-material-extensions" 345 | version = "1.0.3" 346 | description = "Extension pack for Python Markdown." 347 | category = "dev" 348 | optional = false 349 | python-versions = ">=3.6" 350 | 351 | [[package]] 352 | name = "mypy" 353 | version = "0.910" 354 | description = "Optional static typing for Python" 355 | category = "dev" 356 | optional = false 357 | python-versions = ">=3.5" 358 | 359 | [package.dependencies] 360 | mypy-extensions = ">=0.4.3,<0.5.0" 361 | toml = "*" 362 | typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} 363 | typing-extensions = ">=3.7.4" 364 | 365 | [package.extras] 366 | dmypy = ["psutil (>=4.0)"] 367 | python2 = ["typed-ast (>=1.4.0,<1.5.0)"] 368 | 369 | [[package]] 370 | name = "mypy-extensions" 371 | version = "0.4.3" 372 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 373 | category = "main" 374 | optional = false 375 | python-versions = "*" 376 | 377 | [[package]] 378 | name = "packaging" 379 | version = "21.0" 380 | description = "Core utilities for Python packages" 381 | category = "dev" 382 | optional = false 383 | python-versions = ">=3.6" 384 | 385 | [package.dependencies] 386 | pyparsing = ">=2.0.2" 387 | 388 | [[package]] 389 | name = "pathspec" 390 | version = "0.9.0" 391 | description = "Utility library for gitignore style pattern matching of file paths." 392 | category = "dev" 393 | optional = false 394 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 395 | 396 | [[package]] 397 | name = "platformdirs" 398 | version = "2.3.0" 399 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 400 | category = "dev" 401 | optional = false 402 | python-versions = ">=3.6" 403 | 404 | [package.extras] 405 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 406 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 407 | 408 | [[package]] 409 | name = "pluggy" 410 | version = "0.13.1" 411 | description = "plugin and hook calling mechanisms for python" 412 | category = "dev" 413 | optional = false 414 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 415 | 416 | [package.dependencies] 417 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 418 | 419 | [package.extras] 420 | dev = ["pre-commit", "tox"] 421 | 422 | [[package]] 423 | name = "py" 424 | version = "1.10.0" 425 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 426 | category = "dev" 427 | optional = false 428 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 429 | 430 | [[package]] 431 | name = "pycodestyle" 432 | version = "2.7.0" 433 | description = "Python style guide checker" 434 | category = "dev" 435 | optional = false 436 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 437 | 438 | [[package]] 439 | name = "pydantic" 440 | version = "1.8.2" 441 | description = "Data validation and settings management using python 3.6 type hinting" 442 | category = "dev" 443 | optional = false 444 | python-versions = ">=3.6.1" 445 | 446 | [package.dependencies] 447 | typing-extensions = ">=3.7.4.3" 448 | 449 | [package.extras] 450 | dotenv = ["python-dotenv (>=0.10.4)"] 451 | email = ["email-validator (>=1.0.3)"] 452 | 453 | [[package]] 454 | name = "pyflakes" 455 | version = "2.3.1" 456 | description = "passive checker of Python programs" 457 | category = "dev" 458 | optional = false 459 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 460 | 461 | [[package]] 462 | name = "pygments" 463 | version = "2.10.0" 464 | description = "Pygments is a syntax highlighting package written in Python." 465 | category = "dev" 466 | optional = false 467 | python-versions = ">=3.5" 468 | 469 | [[package]] 470 | name = "pymdown-extensions" 471 | version = "8.2" 472 | description = "Extension pack for Python Markdown." 473 | category = "dev" 474 | optional = false 475 | python-versions = ">=3.6" 476 | 477 | [package.dependencies] 478 | Markdown = ">=3.2" 479 | 480 | [[package]] 481 | name = "pyparsing" 482 | version = "2.4.7" 483 | description = "Python parsing module" 484 | category = "dev" 485 | optional = false 486 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 487 | 488 | [[package]] 489 | name = "pytest" 490 | version = "6.2.4" 491 | description = "pytest: simple powerful testing with Python" 492 | category = "dev" 493 | optional = false 494 | python-versions = ">=3.6" 495 | 496 | [package.dependencies] 497 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 498 | attrs = ">=19.2.0" 499 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 500 | importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} 501 | iniconfig = "*" 502 | packaging = "*" 503 | pluggy = ">=0.12,<1.0.0a1" 504 | py = ">=1.8.2" 505 | toml = "*" 506 | 507 | [package.extras] 508 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] 509 | 510 | [[package]] 511 | name = "pytest-cov" 512 | version = "2.12.1" 513 | description = "Pytest plugin for measuring coverage." 514 | category = "dev" 515 | optional = false 516 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 517 | 518 | [package.dependencies] 519 | coverage = ">=5.2.1" 520 | pytest = ">=4.6" 521 | toml = "*" 522 | 523 | [package.extras] 524 | testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] 525 | 526 | [[package]] 527 | name = "python-dateutil" 528 | version = "2.8.2" 529 | description = "Extensions to the standard Python datetime module" 530 | category = "dev" 531 | optional = false 532 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 533 | 534 | [package.dependencies] 535 | six = ">=1.5" 536 | 537 | [[package]] 538 | name = "pyyaml" 539 | version = "5.4.1" 540 | description = "YAML parser and emitter for Python" 541 | category = "main" 542 | optional = false 543 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 544 | 545 | [[package]] 546 | name = "pyyaml-env-tag" 547 | version = "0.1" 548 | description = "A custom YAML tag for referencing environment variables in YAML files. " 549 | category = "dev" 550 | optional = false 551 | python-versions = ">=3.6" 552 | 553 | [package.dependencies] 554 | pyyaml = "*" 555 | 556 | [[package]] 557 | name = "regex" 558 | version = "2021.8.28" 559 | description = "Alternative regular expression module, to replace re." 560 | category = "dev" 561 | optional = false 562 | python-versions = "*" 563 | 564 | [[package]] 565 | name = "requests" 566 | version = "2.26.0" 567 | description = "Python HTTP for Humans." 568 | category = "dev" 569 | optional = false 570 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 571 | 572 | [package.dependencies] 573 | certifi = ">=2017.4.17" 574 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 575 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 576 | urllib3 = ">=1.21.1,<1.27" 577 | 578 | [package.extras] 579 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 580 | use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] 581 | 582 | [[package]] 583 | name = "six" 584 | version = "1.16.0" 585 | description = "Python 2 and 3 compatibility utilities" 586 | category = "dev" 587 | optional = false 588 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 589 | 590 | [[package]] 591 | name = "testfixtures" 592 | version = "6.18.1" 593 | description = "A collection of helpers and mock objects for unit tests and doc tests." 594 | category = "dev" 595 | optional = false 596 | python-versions = "*" 597 | 598 | [package.extras] 599 | build = ["setuptools-git", "twine", "wheel"] 600 | docs = ["django", "django (<2)", "mock", "sphinx", "sybil", "twisted", "zope.component"] 601 | test = ["django", "django (<2)", "mock", "pytest (>=3.6)", "pytest-cov", "pytest-django", "sybil", "twisted", "zope.component"] 602 | 603 | [[package]] 604 | name = "toml" 605 | version = "0.10.2" 606 | description = "Python Library for Tom's Obvious, Minimal Language" 607 | category = "dev" 608 | optional = false 609 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 610 | 611 | [[package]] 612 | name = "tomli" 613 | version = "1.2.1" 614 | description = "A lil' TOML parser" 615 | category = "dev" 616 | optional = false 617 | python-versions = ">=3.6" 618 | 619 | [[package]] 620 | name = "typed-ast" 621 | version = "1.4.3" 622 | description = "a fork of Python 2 and 3 ast modules with type comment support" 623 | category = "dev" 624 | optional = false 625 | python-versions = "*" 626 | 627 | [[package]] 628 | name = "typer" 629 | version = "0.4.0" 630 | description = "Typer, build great CLIs. Easy to code. Based on Python type hints." 631 | category = "main" 632 | optional = false 633 | python-versions = ">=3.6" 634 | 635 | [package.dependencies] 636 | click = ">=7.1.1,<9.0.0" 637 | 638 | [package.extras] 639 | all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] 640 | dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] 641 | doc = ["markdown-include (>=0.5.1,<0.6.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=5.4.0,<6.0.0)"] 642 | test = ["black (>=19.10b0,<20.0b0)", "coverage (>=5.2,<6.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.910)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "shellingham (>=1.3.0,<2.0.0)"] 643 | 644 | [[package]] 645 | name = "types-pyyaml" 646 | version = "5.4.10" 647 | description = "Typing stubs for PyYAML" 648 | category = "dev" 649 | optional = false 650 | python-versions = "*" 651 | 652 | [[package]] 653 | name = "typing-extensions" 654 | version = "3.10.0.2" 655 | description = "Backported and Experimental Type Hints for Python 3.5+" 656 | category = "main" 657 | optional = false 658 | python-versions = "*" 659 | 660 | [[package]] 661 | name = "typing-inspect" 662 | version = "0.7.1" 663 | description = "Runtime inspection utilities for typing module." 664 | category = "main" 665 | optional = false 666 | python-versions = "*" 667 | 668 | [package.dependencies] 669 | mypy-extensions = ">=0.3.0" 670 | typing-extensions = ">=3.7.4" 671 | 672 | [[package]] 673 | name = "urllib3" 674 | version = "1.26.6" 675 | description = "HTTP library with thread-safe connection pooling, file post, and more." 676 | category = "dev" 677 | optional = false 678 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 679 | 680 | [package.extras] 681 | brotli = ["brotlipy (>=0.6.0)"] 682 | secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"] 683 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 684 | 685 | [[package]] 686 | name = "watchdog" 687 | version = "2.1.5" 688 | description = "Filesystem events monitoring" 689 | category = "dev" 690 | optional = false 691 | python-versions = ">=3.6" 692 | 693 | [package.extras] 694 | watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"] 695 | 696 | [[package]] 697 | name = "zipp" 698 | version = "3.5.0" 699 | description = "Backport of pathlib-compatible object wrapper for zip files" 700 | category = "main" 701 | optional = false 702 | python-versions = ">=3.6" 703 | 704 | [package.extras] 705 | docs = ["jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "sphinx"] 706 | testing = ["func-timeout", "jaraco.itertools", "pytest (>=4.6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-flake8", "pytest-mypy"] 707 | 708 | [metadata] 709 | lock-version = "1.1" 710 | python-versions = ">3.7, <4.0" 711 | content-hash = "3948069d851bb18d620b6a3e7aa98fe5bf7891ab77376034684aa834546fc8f1" 712 | 713 | [metadata.files] 714 | atomicwrites = [ 715 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 716 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 717 | ] 718 | attrs = [ 719 | {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, 720 | {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, 721 | ] 722 | black = [ 723 | {file = "black-21.8b0-py3-none-any.whl", hash = "sha256:2a0f9a8c2b2a60dbcf1ccb058842fb22bdbbcb2f32c6cc02d9578f90b92ce8b7"}, 724 | {file = "black-21.8b0.tar.gz", hash = "sha256:570608d28aa3af1792b98c4a337dbac6367877b47b12b88ab42095cfc1a627c2"}, 725 | ] 726 | certifi = [ 727 | {file = "certifi-2022.12.7-py3-none-any.whl", hash = "sha256:4ad3232f5e926d6718ec31cfc1fcadfde020920e278684144551c91769c7bc18"}, 728 | {file = "certifi-2022.12.7.tar.gz", hash = "sha256:35824b4c3a97115964b408844d64aa14db1cc518f6562e8d7261699d1350a9e3"}, 729 | ] 730 | charset-normalizer = [ 731 | {file = "charset-normalizer-2.0.4.tar.gz", hash = "sha256:f23667ebe1084be45f6ae0538e4a5a865206544097e4e8bbcacf42cd02a348f3"}, 732 | {file = "charset_normalizer-2.0.4-py3-none-any.whl", hash = "sha256:0c8911edd15d19223366a194a513099a302055a962bca2cec0f54b8b63175d8b"}, 733 | ] 734 | click = [ 735 | {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"}, 736 | {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"}, 737 | ] 738 | codecov = [ 739 | {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, 740 | {file = "codecov-2.1.12.tar.gz", hash = "sha256:a0da46bb5025426da895af90938def8ee12d37fcbcbbbc15b6dc64cf7ebc51c1"}, 741 | ] 742 | colorama = [ 743 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 744 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 745 | ] 746 | coverage = [ 747 | {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, 748 | {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, 749 | {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, 750 | {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, 751 | {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, 752 | {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, 753 | {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, 754 | {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, 755 | {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, 756 | {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, 757 | {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, 758 | {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, 759 | {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, 760 | {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, 761 | {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, 762 | {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, 763 | {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, 764 | {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, 765 | {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, 766 | {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, 767 | {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, 768 | {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, 769 | {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, 770 | {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, 771 | {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, 772 | {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, 773 | {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, 774 | {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, 775 | {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, 776 | {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, 777 | {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, 778 | {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, 779 | {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, 780 | {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, 781 | {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, 782 | {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, 783 | {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, 784 | {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, 785 | {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, 786 | {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, 787 | {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, 788 | {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, 789 | {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, 790 | {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, 791 | {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, 792 | {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, 793 | {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, 794 | {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, 795 | {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, 796 | {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, 797 | {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, 798 | {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, 799 | ] 800 | flake8 = [ 801 | {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, 802 | {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, 803 | ] 804 | flake8-annotations = [ 805 | {file = "flake8-annotations-2.6.2.tar.gz", hash = "sha256:0d6cd2e770b5095f09689c9d84cc054c51b929c41a68969ea1beb4b825cac515"}, 806 | {file = "flake8_annotations-2.6.2-py3-none-any.whl", hash = "sha256:d10c4638231f8a50c0a597c4efce42bd7b7d85df4f620a0ddaca526138936a4f"}, 807 | ] 808 | flake8-black = [ 809 | {file = "flake8-black-0.2.3.tar.gz", hash = "sha256:c199844bc1b559d91195ebe8620216f21ed67f2cc1ff6884294c91a0d2492684"}, 810 | {file = "flake8_black-0.2.3-py3-none-any.whl", hash = "sha256:cc080ba5b3773b69ba102b6617a00cc4ecbad8914109690cfda4d565ea435d96"}, 811 | ] 812 | flake8-isort = [ 813 | {file = "flake8-isort-4.0.0.tar.gz", hash = "sha256:2b91300f4f1926b396c2c90185844eb1a3d5ec39ea6138832d119da0a208f4d9"}, 814 | {file = "flake8_isort-4.0.0-py2.py3-none-any.whl", hash = "sha256:729cd6ef9ba3659512dee337687c05d79c78e1215fdf921ed67e5fe46cce2f3c"}, 815 | ] 816 | ghp-import = [ 817 | {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"}, 818 | {file = "ghp_import-2.0.1-py3-none-any.whl", hash = "sha256:8241a8e9f8dd3c1fafe9696e6e081b57a208ef907e9939c44e7415e407ab40ea"}, 819 | ] 820 | idna = [ 821 | {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, 822 | {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, 823 | ] 824 | importlib-metadata = [ 825 | {file = "importlib_metadata-4.8.1-py3-none-any.whl", hash = "sha256:b618b6d2d5ffa2f16add5697cf57a46c76a56229b0ed1c438322e4e95645bd15"}, 826 | {file = "importlib_metadata-4.8.1.tar.gz", hash = "sha256:f284b3e11256ad1e5d03ab86bb2ccd6f5339688ff17a4d797a0fe7df326f23b1"}, 827 | ] 828 | iniconfig = [ 829 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 830 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 831 | ] 832 | invoke = [ 833 | {file = "invoke-1.6.0-py2-none-any.whl", hash = "sha256:e6c9917a1e3e73e7ea91fdf82d5f151ccfe85bf30cc65cdb892444c02dbb5f74"}, 834 | {file = "invoke-1.6.0-py3-none-any.whl", hash = "sha256:769e90caeb1bd07d484821732f931f1ad8916a38e3f3e618644687fc09cb6317"}, 835 | {file = "invoke-1.6.0.tar.gz", hash = "sha256:374d1e2ecf78981da94bfaf95366216aaec27c2d6a7b7d5818d92da55aa258d3"}, 836 | ] 837 | isort = [ 838 | {file = "isort-5.9.3-py3-none-any.whl", hash = "sha256:e17d6e2b81095c9db0a03a8025a957f334d6ea30b26f9ec70805411e5c7c81f2"}, 839 | {file = "isort-5.9.3.tar.gz", hash = "sha256:9c2ea1e62d871267b78307fe511c0838ba0da28698c5732d54e2790bf3ba9899"}, 840 | ] 841 | jinja2 = [ 842 | {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, 843 | {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, 844 | ] 845 | libcst = [ 846 | {file = "libcst-0.3.23-py3-none-any.whl", hash = "sha256:2e1f77fbaaff93b889376c92f588b718edbdc21f956abbe27d10dfd1ff2d76c3"}, 847 | {file = "libcst-0.3.23.tar.gz", hash = "sha256:330f9082a309bad808e283e80845a843200303bb256690185b98ca458a62c4f8"}, 848 | ] 849 | markdown = [ 850 | {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, 851 | {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, 852 | ] 853 | markupsafe = [ 854 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d8446c54dc28c01e5a2dbac5a25f071f6653e6e40f3a8818e8b45d790fe6ef53"}, 855 | {file = "MarkupSafe-2.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:36bc903cbb393720fad60fc28c10de6acf10dc6cc883f3e24ee4012371399a38"}, 856 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d7d807855b419fc2ed3e631034685db6079889a1f01d5d9dac950f764da3dad"}, 857 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:add36cb2dbb8b736611303cd3bfcee00afd96471b09cda130da3581cbdc56a6d"}, 858 | {file = "MarkupSafe-2.0.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:168cd0a3642de83558a5153c8bd34f175a9a6e7f6dc6384b9655d2697312a646"}, 859 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4dc8f9fb58f7364b63fd9f85013b780ef83c11857ae79f2feda41e270468dd9b"}, 860 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20dca64a3ef2d6e4d5d615a3fd418ad3bde77a47ec8a23d984a12b5b4c74491a"}, 861 | {file = "MarkupSafe-2.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cdfba22ea2f0029c9261a4bd07e830a8da012291fbe44dc794e488b6c9bb353a"}, 862 | {file = "MarkupSafe-2.0.1-cp310-cp310-win32.whl", hash = "sha256:99df47edb6bda1249d3e80fdabb1dab8c08ef3975f69aed437cb69d0a5de1e28"}, 863 | {file = "MarkupSafe-2.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:e0f138900af21926a02425cf736db95be9f4af72ba1bb21453432a07f6082134"}, 864 | {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, 865 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, 866 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, 867 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, 868 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, 869 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, 870 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf5d821ffabf0ef3533c39c518f3357b171a1651c1ff6827325e4489b0e46c3c"}, 871 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0d4b31cc67ab36e3392bbf3862cfbadac3db12bdd8b02a2731f509ed5b829724"}, 872 | {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:baa1a4e8f868845af802979fcdbf0bb11f94f1cb7ced4c4b8a351bb60d108145"}, 873 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:deb993cacb280823246a026e3b2d81c493c53de6acfd5e6bfe31ab3402bb37dd"}, 874 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:63f3268ba69ace99cab4e3e3b5840b03340efed0948ab8f78d2fd87ee5442a4f"}, 875 | {file = "MarkupSafe-2.0.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d206346619592c6200148b01a2142798c989edcb9c896f9ac9722a99d4e77e6"}, 876 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, 877 | {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, 878 | {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, 879 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, 880 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, 881 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, 882 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, 883 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, 884 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9936f0b261d4df76ad22f8fee3ae83b60d7c3e871292cd42f40b81b70afae85"}, 885 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2a7d351cbd8cfeb19ca00de495e224dea7e7d919659c2841bbb7f420ad03e2d6"}, 886 | {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:60bf42e36abfaf9aff1f50f52644b336d4f0a3fd6d8a60ca0d054ac9f713a864"}, 887 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d6c7ebd4e944c85e2c3421e612a7057a2f48d478d79e61800d81468a8d842207"}, 888 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f0567c4dc99f264f49fe27da5f735f414c4e7e7dd850cfd8e69f0862d7c74ea9"}, 889 | {file = "MarkupSafe-2.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:89c687013cb1cd489a0f0ac24febe8c7a666e6e221b783e53ac50ebf68e45d86"}, 890 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, 891 | {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, 892 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5bb28c636d87e840583ee3adeb78172efc47c8b26127267f54a9c0ec251d41a9"}, 893 | {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, 894 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, 895 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, 896 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, 897 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, 898 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, 899 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fcf051089389abe060c9cd7caa212c707e58153afa2c649f00346ce6d260f1b"}, 900 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5855f8438a7d1d458206a2466bf82b0f104a3724bf96a1c781ab731e4201731a"}, 901 | {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3dd007d54ee88b46be476e293f48c85048603f5f516008bee124ddd891398ed6"}, 902 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aca6377c0cb8a8253e493c6b451565ac77e98c2951c45f913e0b52facdcff83f"}, 903 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:04635854b943835a6ea959e948d19dcd311762c5c0c6e1f0e16ee57022669194"}, 904 | {file = "MarkupSafe-2.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6300b8454aa6930a24b9618fbb54b5a68135092bc666f7b06901f897fa5c2fee"}, 905 | {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, 906 | {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, 907 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, 908 | {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, 909 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, 910 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, 911 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, 912 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, 913 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, 914 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c47adbc92fc1bb2b3274c4b3a43ae0e4573d9fbff4f54cd484555edbf030baf1"}, 915 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:37205cac2a79194e3750b0af2a5720d95f786a55ce7df90c3af697bfa100eaac"}, 916 | {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1f2ade76b9903f39aa442b4aadd2177decb66525062db244b35d71d0ee8599b6"}, 917 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4296f2b1ce8c86a6aea78613c34bb1a672ea0e3de9c6ba08a960efe0b0a09047"}, 918 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f02365d4e99430a12647f09b6cc8bab61a6564363f313126f775eb4f6ef798e"}, 919 | {file = "MarkupSafe-2.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5b6d930f030f8ed98e3e6c98ffa0652bdb82601e7a016ec2ab5d7ff23baa78d1"}, 920 | {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, 921 | {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, 922 | {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, 923 | ] 924 | mccabe = [ 925 | {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, 926 | {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, 927 | ] 928 | mergedeep = [ 929 | {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, 930 | {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, 931 | ] 932 | mkdocs = [ 933 | {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, 934 | {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, 935 | ] 936 | mkdocs-material = [ 937 | {file = "mkdocs-material-7.2.6.tar.gz", hash = "sha256:4bdeff63904680865676ceb3193216934de0b33fa5b2446e0a84ade60929ee54"}, 938 | {file = "mkdocs_material-7.2.6-py2.py3-none-any.whl", hash = "sha256:4c6939b9d7d5c6db948ab02df8525c64211828ddf33286acea8b9d2115cec369"}, 939 | ] 940 | mkdocs-material-extensions = [ 941 | {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, 942 | {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, 943 | ] 944 | mypy = [ 945 | {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, 946 | {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, 947 | {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, 948 | {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, 949 | {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, 950 | {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, 951 | {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, 952 | {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, 953 | {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, 954 | {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, 955 | {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, 956 | {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, 957 | {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, 958 | {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, 959 | {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, 960 | {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, 961 | {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, 962 | {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, 963 | {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, 964 | {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, 965 | {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, 966 | {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, 967 | {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, 968 | ] 969 | mypy-extensions = [ 970 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 971 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 972 | ] 973 | packaging = [ 974 | {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, 975 | {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, 976 | ] 977 | pathspec = [ 978 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 979 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 980 | ] 981 | platformdirs = [ 982 | {file = "platformdirs-2.3.0-py3-none-any.whl", hash = "sha256:8003ac87717ae2c7ee1ea5a84a1a61e87f3fbd16eb5aadba194ea30a9019f648"}, 983 | {file = "platformdirs-2.3.0.tar.gz", hash = "sha256:15b056538719b1c94bdaccb29e5f81879c7f7f0f4a153f46086d155dffcd4f0f"}, 984 | ] 985 | pluggy = [ 986 | {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, 987 | {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, 988 | ] 989 | py = [ 990 | {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, 991 | {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, 992 | ] 993 | pycodestyle = [ 994 | {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, 995 | {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, 996 | ] 997 | pydantic = [ 998 | {file = "pydantic-1.8.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:05ddfd37c1720c392f4e0d43c484217b7521558302e7069ce8d318438d297739"}, 999 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:a7c6002203fe2c5a1b5cbb141bb85060cbff88c2d78eccbc72d97eb7022c43e4"}, 1000 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:589eb6cd6361e8ac341db97602eb7f354551482368a37f4fd086c0733548308e"}, 1001 | {file = "pydantic-1.8.2-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:10e5622224245941efc193ad1d159887872776df7a8fd592ed746aa25d071840"}, 1002 | {file = "pydantic-1.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:99a9fc39470010c45c161a1dc584997f1feb13f689ecf645f59bb4ba623e586b"}, 1003 | {file = "pydantic-1.8.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a83db7205f60c6a86f2c44a61791d993dff4b73135df1973ecd9eed5ea0bda20"}, 1004 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:41b542c0b3c42dc17da70554bc6f38cbc30d7066d2c2815a94499b5684582ecb"}, 1005 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:ea5cb40a3b23b3265f6325727ddfc45141b08ed665458be8c6285e7b85bd73a1"}, 1006 | {file = "pydantic-1.8.2-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:18b5ea242dd3e62dbf89b2b0ec9ba6c7b5abaf6af85b95a97b00279f65845a23"}, 1007 | {file = "pydantic-1.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:234a6c19f1c14e25e362cb05c68afb7f183eb931dd3cd4605eafff055ebbf287"}, 1008 | {file = "pydantic-1.8.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:021ea0e4133e8c824775a0cfe098677acf6fa5a3cbf9206a376eed3fc09302cd"}, 1009 | {file = "pydantic-1.8.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e710876437bc07bd414ff453ac8ec63d219e7690128d925c6e82889d674bb505"}, 1010 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:ac8eed4ca3bd3aadc58a13c2aa93cd8a884bcf21cb019f8cfecaae3b6ce3746e"}, 1011 | {file = "pydantic-1.8.2-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4a03cbbe743e9c7247ceae6f0d8898f7a64bb65800a45cbdc52d65e370570820"}, 1012 | {file = "pydantic-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:8621559dcf5afacf0069ed194278f35c255dc1a1385c28b32dd6c110fd6531b3"}, 1013 | {file = "pydantic-1.8.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8b223557f9510cf0bfd8b01316bf6dd281cf41826607eada99662f5e4963f316"}, 1014 | {file = "pydantic-1.8.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:244ad78eeb388a43b0c927e74d3af78008e944074b7d0f4f696ddd5b2af43c62"}, 1015 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:05ef5246a7ffd2ce12a619cbb29f3307b7c4509307b1b49f456657b43529dc6f"}, 1016 | {file = "pydantic-1.8.2-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:54cd5121383f4a461ff7644c7ca20c0419d58052db70d8791eacbbe31528916b"}, 1017 | {file = "pydantic-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:4be75bebf676a5f0f87937c6ddb061fa39cbea067240d98e298508c1bda6f3f3"}, 1018 | {file = "pydantic-1.8.2-py3-none-any.whl", hash = "sha256:fec866a0b59f372b7e776f2d7308511784dace622e0992a0b59ea3ccee0ae833"}, 1019 | {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, 1020 | ] 1021 | pyflakes = [ 1022 | {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, 1023 | {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, 1024 | ] 1025 | pygments = [ 1026 | {file = "Pygments-2.10.0-py3-none-any.whl", hash = "sha256:b8e67fe6af78f492b3c4b3e2970c0624cbf08beb1e493b2c99b9fa1b67a20380"}, 1027 | {file = "Pygments-2.10.0.tar.gz", hash = "sha256:f398865f7eb6874156579fdf36bc840a03cab64d1cde9e93d68f46a425ec52c6"}, 1028 | ] 1029 | pymdown-extensions = [ 1030 | {file = "pymdown-extensions-8.2.tar.gz", hash = "sha256:b6daa94aad9e1310f9c64c8b1f01e4ce82937ab7eb53bfc92876a97aca02a6f4"}, 1031 | {file = "pymdown_extensions-8.2-py3-none-any.whl", hash = "sha256:141452d8ed61165518f2c923454bf054866b85cf466feedb0eb68f04acdc2560"}, 1032 | ] 1033 | pyparsing = [ 1034 | {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, 1035 | {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, 1036 | ] 1037 | pytest = [ 1038 | {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, 1039 | {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, 1040 | ] 1041 | pytest-cov = [ 1042 | {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, 1043 | {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, 1044 | ] 1045 | python-dateutil = [ 1046 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 1047 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 1048 | ] 1049 | pyyaml = [ 1050 | {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, 1051 | {file = "PyYAML-5.4.1-cp27-cp27m-win32.whl", hash = "sha256:129def1b7c1bf22faffd67b8f3724645203b79d8f4cc81f674654d9902cb4393"}, 1052 | {file = "PyYAML-5.4.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4465124ef1b18d9ace298060f4eccc64b0850899ac4ac53294547536533800c8"}, 1053 | {file = "PyYAML-5.4.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:bb4191dfc9306777bc594117aee052446b3fa88737cd13b7188d0e7aa8162185"}, 1054 | {file = "PyYAML-5.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:6c78645d400265a062508ae399b60b8c167bf003db364ecb26dcab2bda048253"}, 1055 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:4e0583d24c881e14342eaf4ec5fbc97f934b999a6828693a99157fde912540cc"}, 1056 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:72a01f726a9c7851ca9bfad6fd09ca4e090a023c00945ea05ba1638c09dc3347"}, 1057 | {file = "PyYAML-5.4.1-cp36-cp36m-manylinux2014_s390x.whl", hash = "sha256:895f61ef02e8fed38159bb70f7e100e00f471eae2bc838cd0f4ebb21e28f8541"}, 1058 | {file = "PyYAML-5.4.1-cp36-cp36m-win32.whl", hash = "sha256:3bd0e463264cf257d1ffd2e40223b197271046d09dadf73a0fe82b9c1fc385a5"}, 1059 | {file = "PyYAML-5.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:e4fac90784481d221a8e4b1162afa7c47ed953be40d31ab4629ae917510051df"}, 1060 | {file = "PyYAML-5.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5accb17103e43963b80e6f837831f38d314a0495500067cb25afab2e8d7a4018"}, 1061 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e1d4970ea66be07ae37a3c2e48b5ec63f7ba6804bdddfdbd3cfd954d25a82e63"}, 1062 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:cb333c16912324fd5f769fff6bc5de372e9e7a202247b48870bc251ed40239aa"}, 1063 | {file = "PyYAML-5.4.1-cp37-cp37m-manylinux2014_s390x.whl", hash = "sha256:fe69978f3f768926cfa37b867e3843918e012cf83f680806599ddce33c2c68b0"}, 1064 | {file = "PyYAML-5.4.1-cp37-cp37m-win32.whl", hash = "sha256:dd5de0646207f053eb0d6c74ae45ba98c3395a571a2891858e87df7c9b9bd51b"}, 1065 | {file = "PyYAML-5.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:08682f6b72c722394747bddaf0aa62277e02557c0fd1c42cb853016a38f8dedf"}, 1066 | {file = "PyYAML-5.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d2d9808ea7b4af864f35ea216be506ecec180628aced0704e34aca0b040ffe46"}, 1067 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8c1be557ee92a20f184922c7b6424e8ab6691788e6d86137c5d93c1a6ec1b8fb"}, 1068 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:fd7f6999a8070df521b6384004ef42833b9bd62cfee11a09bda1079b4b704247"}, 1069 | {file = "PyYAML-5.4.1-cp38-cp38-manylinux2014_s390x.whl", hash = "sha256:bfb51918d4ff3d77c1c856a9699f8492c612cde32fd3bcd344af9be34999bfdc"}, 1070 | {file = "PyYAML-5.4.1-cp38-cp38-win32.whl", hash = "sha256:fa5ae20527d8e831e8230cbffd9f8fe952815b2b7dae6ffec25318803a7528fc"}, 1071 | {file = "PyYAML-5.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:0f5f5786c0e09baddcd8b4b45f20a7b5d61a7e7e99846e3c799b05c7c53fa696"}, 1072 | {file = "PyYAML-5.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:294db365efa064d00b8d1ef65d8ea2c3426ac366c0c4368d930bf1c5fb497f77"}, 1073 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:74c1485f7707cf707a7aef42ef6322b8f97921bd89be2ab6317fd782c2d53183"}, 1074 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d483ad4e639292c90170eb6f7783ad19490e7a8defb3e46f97dfe4bacae89122"}, 1075 | {file = "PyYAML-5.4.1-cp39-cp39-manylinux2014_s390x.whl", hash = "sha256:fdc842473cd33f45ff6bce46aea678a54e3d21f1b61a7750ce3c498eedfe25d6"}, 1076 | {file = "PyYAML-5.4.1-cp39-cp39-win32.whl", hash = "sha256:49d4cdd9065b9b6e206d0595fee27a96b5dd22618e7520c33204a4a3239d5b10"}, 1077 | {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, 1078 | {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, 1079 | ] 1080 | pyyaml-env-tag = [ 1081 | {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, 1082 | {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, 1083 | ] 1084 | regex = [ 1085 | {file = "regex-2021.8.28-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9d05ad5367c90814099000442b2125535e9d77581855b9bee8780f1b41f2b1a2"}, 1086 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3bf1bc02bc421047bfec3343729c4bbbea42605bcfd6d6bfe2c07ade8b12d2a"}, 1087 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f6a808044faae658f546dd5f525e921de9fa409de7a5570865467f03a626fc0"}, 1088 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a617593aeacc7a691cc4af4a4410031654f2909053bd8c8e7db837f179a630eb"}, 1089 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:79aef6b5cd41feff359acaf98e040844613ff5298d0d19c455b3d9ae0bc8c35a"}, 1090 | {file = "regex-2021.8.28-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0fc1f8f06977c2d4f5e3d3f0d4a08089be783973fc6b6e278bde01f0544ff308"}, 1091 | {file = "regex-2021.8.28-cp310-cp310-win32.whl", hash = "sha256:6eebf512aa90751d5ef6a7c2ac9d60113f32e86e5687326a50d7686e309f66ed"}, 1092 | {file = "regex-2021.8.28-cp310-cp310-win_amd64.whl", hash = "sha256:ac88856a8cbccfc14f1b2d0b829af354cc1743cb375e7f04251ae73b2af6adf8"}, 1093 | {file = "regex-2021.8.28-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c206587c83e795d417ed3adc8453a791f6d36b67c81416676cad053b4104152c"}, 1094 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8690ed94481f219a7a967c118abaf71ccc440f69acd583cab721b90eeedb77c"}, 1095 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:328a1fad67445550b982caa2a2a850da5989fd6595e858f02d04636e7f8b0b13"}, 1096 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c7cb4c512d2d3b0870e00fbbac2f291d4b4bf2634d59a31176a87afe2777c6f0"}, 1097 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:66256b6391c057305e5ae9209941ef63c33a476b73772ca967d4a2df70520ec1"}, 1098 | {file = "regex-2021.8.28-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8e44769068d33e0ea6ccdf4b84d80c5afffe5207aa4d1881a629cf0ef3ec398f"}, 1099 | {file = "regex-2021.8.28-cp36-cp36m-win32.whl", hash = "sha256:08d74bfaa4c7731b8dac0a992c63673a2782758f7cfad34cf9c1b9184f911354"}, 1100 | {file = "regex-2021.8.28-cp36-cp36m-win_amd64.whl", hash = "sha256:abb48494d88e8a82601af905143e0de838c776c1241d92021e9256d5515b3645"}, 1101 | {file = "regex-2021.8.28-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b4c220a1fe0d2c622493b0a1fd48f8f991998fb447d3cd368033a4b86cf1127a"}, 1102 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4a332404baa6665b54e5d283b4262f41f2103c255897084ec8f5487ce7b9e8e"}, 1103 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c61dcc1cf9fd165127a2853e2c31eb4fb961a4f26b394ac9fe5669c7a6592892"}, 1104 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ee329d0387b5b41a5dddbb6243a21cb7896587a651bebb957e2d2bb8b63c0791"}, 1105 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f60667673ff9c249709160529ab39667d1ae9fd38634e006bec95611f632e759"}, 1106 | {file = "regex-2021.8.28-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b844fb09bd9936ed158ff9df0ab601e2045b316b17aa8b931857365ea8586906"}, 1107 | {file = "regex-2021.8.28-cp37-cp37m-win32.whl", hash = "sha256:4cde065ab33bcaab774d84096fae266d9301d1a2f5519d7bd58fc55274afbf7a"}, 1108 | {file = "regex-2021.8.28-cp37-cp37m-win_amd64.whl", hash = "sha256:1413b5022ed6ac0d504ba425ef02549a57d0f4276de58e3ab7e82437892704fc"}, 1109 | {file = "regex-2021.8.28-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ed4b50355b066796dacdd1cf538f2ce57275d001838f9b132fab80b75e8c84dd"}, 1110 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28fc475f560d8f67cc8767b94db4c9440210f6958495aeae70fac8faec631797"}, 1111 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdc178caebd0f338d57ae445ef8e9b737ddf8fbc3ea187603f65aec5b041248f"}, 1112 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:999ad08220467b6ad4bd3dd34e65329dd5d0df9b31e47106105e407954965256"}, 1113 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:808ee5834e06f57978da3e003ad9d6292de69d2bf6263662a1a8ae30788e080b"}, 1114 | {file = "regex-2021.8.28-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d5111d4c843d80202e62b4fdbb4920db1dcee4f9366d6b03294f45ed7b18b42e"}, 1115 | {file = "regex-2021.8.28-cp38-cp38-win32.whl", hash = "sha256:473858730ef6d6ff7f7d5f19452184cd0caa062a20047f6d6f3e135a4648865d"}, 1116 | {file = "regex-2021.8.28-cp38-cp38-win_amd64.whl", hash = "sha256:31a99a4796bf5aefc8351e98507b09e1b09115574f7c9dbb9cf2111f7220d2e2"}, 1117 | {file = "regex-2021.8.28-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:04f6b9749e335bb0d2f68c707f23bb1773c3fb6ecd10edf0f04df12a8920d468"}, 1118 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b006628fe43aa69259ec04ca258d88ed19b64791693df59c422b607b6ece8bb"}, 1119 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:121f4b3185feaade3f85f70294aef3f777199e9b5c0c0245c774ae884b110a2d"}, 1120 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a577a21de2ef8059b58f79ff76a4da81c45a75fe0bfb09bc8b7bb4293fa18983"}, 1121 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1743345e30917e8c574f273f51679c294effba6ad372db1967852f12c76759d8"}, 1122 | {file = "regex-2021.8.28-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e1e8406b895aba6caa63d9fd1b6b1700d7e4825f78ccb1e5260551d168db38ed"}, 1123 | {file = "regex-2021.8.28-cp39-cp39-win32.whl", hash = "sha256:ed283ab3a01d8b53de3a05bfdf4473ae24e43caee7dcb5584e86f3f3e5ab4374"}, 1124 | {file = "regex-2021.8.28-cp39-cp39-win_amd64.whl", hash = "sha256:610b690b406653c84b7cb6091facb3033500ee81089867ee7d59e675f9ca2b73"}, 1125 | {file = "regex-2021.8.28.tar.gz", hash = "sha256:f585cbbeecb35f35609edccb95efd95a3e35824cd7752b586503f7e6087303f1"}, 1126 | ] 1127 | requests = [ 1128 | {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, 1129 | {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, 1130 | ] 1131 | six = [ 1132 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 1133 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 1134 | ] 1135 | testfixtures = [ 1136 | {file = "testfixtures-6.18.1-py2.py3-none-any.whl", hash = "sha256:486be7b01eb71326029811878a3317b7e7994324621c0ec633c8e24499d8d5b3"}, 1137 | {file = "testfixtures-6.18.1.tar.gz", hash = "sha256:0a6422737f6d89b45cdef1e2df5576f52ad0f507956002ce1020daa9f44211d6"}, 1138 | ] 1139 | toml = [ 1140 | {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, 1141 | {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, 1142 | ] 1143 | tomli = [ 1144 | {file = "tomli-1.2.1-py3-none-any.whl", hash = "sha256:8dd0e9524d6f386271a36b41dbf6c57d8e32fd96fd22b6584679dc569d20899f"}, 1145 | {file = "tomli-1.2.1.tar.gz", hash = "sha256:a5b75cb6f3968abb47af1b40c1819dc519ea82bcc065776a866e8d74c5ca9442"}, 1146 | ] 1147 | typed-ast = [ 1148 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, 1149 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, 1150 | {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, 1151 | {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, 1152 | {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, 1153 | {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, 1154 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, 1155 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, 1156 | {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, 1157 | {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, 1158 | {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, 1159 | {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, 1160 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, 1161 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, 1162 | {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, 1163 | {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, 1164 | {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, 1165 | {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, 1166 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, 1167 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, 1168 | {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, 1169 | {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, 1170 | {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, 1171 | {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, 1172 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, 1173 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, 1174 | {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, 1175 | {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, 1176 | {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, 1177 | {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, 1178 | ] 1179 | typer = [ 1180 | {file = "typer-0.4.0-py3-none-any.whl", hash = "sha256:d81169725140423d072df464cad1ff25ee154ef381aaf5b8225352ea187ca338"}, 1181 | {file = "typer-0.4.0.tar.gz", hash = "sha256:63c3aeab0549750ffe40da79a1b524f60e08a2cbc3126c520ebf2eeaf507f5dd"}, 1182 | ] 1183 | types-pyyaml = [ 1184 | {file = "types-PyYAML-5.4.10.tar.gz", hash = "sha256:1d9e431e9f1f78a65ea957c558535a3b15ad67ea4912bce48a6c1b613dcf81ad"}, 1185 | {file = "types_PyYAML-5.4.10-py3-none-any.whl", hash = "sha256:f1d1357168988e45fa20c65aecb3911462246a84809015dd889ebf8b1db74124"}, 1186 | ] 1187 | typing-extensions = [ 1188 | {file = "typing_extensions-3.10.0.2-py2-none-any.whl", hash = "sha256:d8226d10bc02a29bcc81df19a26e56a9647f8b0a6d4a83924139f4a8b01f17b7"}, 1189 | {file = "typing_extensions-3.10.0.2-py3-none-any.whl", hash = "sha256:f1d25edafde516b146ecd0613dabcc61409817af4766fbbcfb8d1ad4ec441a34"}, 1190 | {file = "typing_extensions-3.10.0.2.tar.gz", hash = "sha256:49f75d16ff11f1cd258e1b988ccff82a3ca5570217d7ad8c5f48205dd99a677e"}, 1191 | ] 1192 | typing-inspect = [ 1193 | {file = "typing_inspect-0.7.1-py2-none-any.whl", hash = "sha256:b1f56c0783ef0f25fb064a01be6e5407e54cf4a4bf4f3ba3fe51e0bd6dcea9e5"}, 1194 | {file = "typing_inspect-0.7.1-py3-none-any.whl", hash = "sha256:3cd7d4563e997719a710a3bfe7ffb544c6b72069b6812a02e9b414a8fa3aaa6b"}, 1195 | {file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"}, 1196 | ] 1197 | urllib3 = [ 1198 | {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, 1199 | {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, 1200 | ] 1201 | watchdog = [ 1202 | {file = "watchdog-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:5f57ce4f7e498278fb2a091f39359930144a0f2f90ea8cbf4523c4e25de34028"}, 1203 | {file = "watchdog-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b74d0d92a69a7ab5f101f9fe74e44ba017be269efa824337366ccbb4effde85"}, 1204 | {file = "watchdog-2.1.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:59767f476cd1f48531bf378f0300565d879688c82da8369ca8c52f633299523c"}, 1205 | {file = "watchdog-2.1.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:814d396859c95598f7576d15bc257c3bd3ba61fa4bc1db7dfc18f09070ded7da"}, 1206 | {file = "watchdog-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:28777dbed3bbd95f9c70f461443990a36c07dbf49ae7cd69932cdd1b8fb2850c"}, 1207 | {file = "watchdog-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5cf78f794c9d7bc64a626ef4f71aff88f57a7ae288e0b359a9c6ea711a41395f"}, 1208 | {file = "watchdog-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:43bf728eb7830559f329864ab5da2302c15b2efbac24ad84ccc09949ba753c40"}, 1209 | {file = "watchdog-2.1.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:a7053d4d22dc95c5e0c90aeeae1e4ed5269d2f04001798eec43a654a03008d22"}, 1210 | {file = "watchdog-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:6f3ad1d973fe8fc8fe64ba38f6a934b74346342fa98ef08ad5da361a05d46044"}, 1211 | {file = "watchdog-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:41d44ef21a77a32b55ce9bf59b75777063751f688de51098859b7c7f6466589a"}, 1212 | {file = "watchdog-2.1.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ed4ca4351cd2bb0d863ee737a2011ca44d8d8be19b43509bd4507f8a449b376b"}, 1213 | {file = "watchdog-2.1.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8874d5ad6b7f43b18935d9b0183e29727a623a216693d6938d07dfd411ba462f"}, 1214 | {file = "watchdog-2.1.5-py3-none-manylinux2014_aarch64.whl", hash = "sha256:50a7f81f99d238f72185f481b493f9de80096e046935b60ea78e1276f3d76960"}, 1215 | {file = "watchdog-2.1.5-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e40e33a4889382824846b4baa05634e1365b47c6fa40071dc2d06b4d7c715fc1"}, 1216 | {file = "watchdog-2.1.5-py3-none-manylinux2014_i686.whl", hash = "sha256:78b1514067ff4089f4dac930b043a142997a5b98553120919005e97fbaba6546"}, 1217 | {file = "watchdog-2.1.5-py3-none-manylinux2014_ppc64.whl", hash = "sha256:58ae842300cbfe5e62fb068c83901abe76e4f413234b7bec5446e4275eb1f9cb"}, 1218 | {file = "watchdog-2.1.5-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:b0cc7d8b7d60da6c313779d85903ce39a63d89d866014b085f720a083d5f3e9a"}, 1219 | {file = "watchdog-2.1.5-py3-none-manylinux2014_s390x.whl", hash = "sha256:e60d3bb7166b7cb830b86938d1eb0e6cfe23dfd634cce05c128f8f9967895193"}, 1220 | {file = "watchdog-2.1.5-py3-none-manylinux2014_x86_64.whl", hash = "sha256:51af09ae937ada0e9a10cc16988ec03c649754a91526170b6839b89fc56d6acb"}, 1221 | {file = "watchdog-2.1.5-py3-none-win32.whl", hash = "sha256:9391003635aa783957b9b11175d9802d3272ed67e69ef2e3394c0b6d9d24fa9a"}, 1222 | {file = "watchdog-2.1.5-py3-none-win_amd64.whl", hash = "sha256:eab14adfc417c2c983fbcb2c73ef3f28ba6990d1fff45d1180bf7e38bda0d98d"}, 1223 | {file = "watchdog-2.1.5-py3-none-win_ia64.whl", hash = "sha256:a2888a788893c4ef7e562861ec5433875b7915f930a5a7ed3d32c048158f1be5"}, 1224 | {file = "watchdog-2.1.5.tar.gz", hash = "sha256:5563b005907613430ef3d4aaac9c78600dd5704e84764cb6deda4b3d72807f09"}, 1225 | ] 1226 | zipp = [ 1227 | {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, 1228 | {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, 1229 | ] 1230 | --------------------------------------------------------------------------------