From 8a9c001e6a99a25da85f1fc4ced071dbbcf43e04 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 15:17:38 -0400 Subject: [PATCH 01/20] Port to Poetry and Nox --- .dockerignore | 4 +- .github/workflows/python.yml | 14 +- .gitignore | 9 +- Dockerfile | 19 +- MANIFEST.in | 5 - dev-requirements.in | 35 - dev-requirements.txt | 199 ----- noxfile.py | 98 +++ poetry.lock | 1428 ++++++++++++++++++++++++++++++++++ pyproject.toml | 85 +- requirements.in | 2 - requirements.txt | 14 - setup.py | 56 -- src/fact/cli.py | 6 +- tox.ini | 11 - 15 files changed, 1637 insertions(+), 348 deletions(-) delete mode 100644 MANIFEST.in delete mode 100644 dev-requirements.in delete mode 100644 dev-requirements.txt create mode 100644 noxfile.py create mode 100644 poetry.lock delete mode 100644 requirements.in delete mode 100644 requirements.txt delete mode 100755 setup.py diff --git a/.dockerignore b/.dockerignore index de3c086..a70d3fa 100644 --- a/.dockerignore +++ b/.dockerignore @@ -4,11 +4,9 @@ # Except the following. !src !LICENSE.txt -!MANIFEST.in !pyproject.toml +!poetry.lock !README.md -!requirements.txt -!setup.py # Filter unwanted files from included folders. **/__pycache__ diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 9f8a8fa..83b437e 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -5,18 +5,12 @@ on: [push, pull_request] jobs: tox: runs-on: ubuntu-latest - strategy: - matrix: - python-version: [ "3.8", "3.9", "3.10" ] steps: - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} + - uses: excitedleigh/setup-nox@v2.0.0 - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install tox tox-gh-actions - - name: Test with tox - run: tox + python -m pip install nox-poetry + - name: Test with Nox + run: nox diff --git a/.gitignore b/.gitignore index cd5f1b4..349ff56 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,13 @@ -*.egg-info -.tox .idea +*.egg-info +.mypy_cache +.nox +.pytest_cache __pycache__ .coverage .coverage.* htmlcov *.pyc -venv dist -.pytest_cache -.mypy_cache build site diff --git a/Dockerfile b/Dockerfile index 8ce26cd..a0f80eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,9 @@ ENV WORKDIR /src # absoulte symlinks in it. ENV VIRTUAL_ENV /opt/venv +# Pin Poetry to a specific version to make Docker builds reproducible. +ENV POETRY_VERSION 1.1.13 + WORKDIR ${WORKDIR} # Install any system depdendencies required to build wheels, such as C compilers or system packages @@ -31,21 +34,29 @@ WORKDIR ${WORKDIR} # gcc \ # && rm -rf /var/lib/apt/lists/* +# Install Poetry into the global environment to isolate it from the venv. This prevents Poetry +# from uninstalling parts of itself. +RUN pip install "poetry==${POETRY_VERSION}" + +# Copy in project dependency specification. +COPY pyproject.toml poetry.lock ./ +RUN poetry export --format requirements.txt --output requirements.txt + # Pre-download/compile wheel dependencies into a virtual environment. # Doing this in a multi-stage build allows ommitting compile dependencies from the final image. RUN python -m venv ${VIRTUAL_ENV} ENV PATH "${VIRTUAL_ENV}/bin:${PATH}" -COPY requirements.txt ${WORKDIR} RUN pip install --upgrade pip wheel && \ pip install -r requirements.txt # Copy in source files. -COPY LICENSE.txt MANIFEST.in pyproject.toml README.md requirements.txt setup.py ./ +COPY README.md ./ COPY src src -# Install console script. -RUN pip install . +# Don't install the package itself with Poetry becuase it will install it as an editable install. +RUN poetry build && \ + pip install dist/*.whl ## Final Image # The image used in the final image MUST match exactly to the python_builder image. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index c1e9223..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,5 +0,0 @@ -include README.md -include LICENSE.txt -include requirements.txt -include src/fact/py.typed -recursive-exclude tests * diff --git a/dev-requirements.in b/dev-requirements.in deleted file mode 100644 index d862eac..0000000 --- a/dev-requirements.in +++ /dev/null @@ -1,35 +0,0 @@ -# Constrain dev-requirements by requirements to avoid clashing requirements. --c requirements.txt - -tox - -# Testing. -pytest -pytest-cov - -# Type checking. -mypy -# As of mypy 0.900, mypy no longer bundles the stubs for third-party libraries that reside -# in the typeshed project. Add these "types-" packages here if you depend on them in -# requirements.in (e.g. types-requests). -# See: http://mypy-lang.blogspot.com/2021/06/mypy-0900-released.html -# https://github.com/python/typeshed/tree/master/stubs - -# Linting. -flake8 -flake8-bugbear -flake8-broken-line -flake8-comprehensions -pep8-naming - -# Formatting. -black -isort - -# Documentation. -mkdocs-material -mkdocs-htmlproofer-plugin -mkdocstrings[python] -## Autodoc. -mkdocs-gen-files -mkdocs-literate-nav diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index b479f6a..0000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,199 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile dev-requirements.in -# -attrs==21.4.0 - # via - # flake8-bugbear - # pytest -beautifulsoup4==4.10.0 - # via mkdocs-htmlproofer-plugin -black==22.1.0 - # via -r dev-requirements.in -certifi==2021.10.8 - # via requests -charset-normalizer==2.0.12 - # via requests -click==8.0.4 - # via - # -c requirements.txt - # black - # mkdocs -coverage[toml]==6.3.2 - # via pytest-cov -distlib==0.3.4 - # via virtualenv -filelock==3.6.0 - # via - # tox - # virtualenv -flake8==4.0.1 - # via - # -r dev-requirements.in - # flake8-broken-line - # flake8-bugbear - # flake8-comprehensions - # flake8-polyfill - # pep8-naming -flake8-broken-line==0.4.0 - # via -r dev-requirements.in -flake8-bugbear==22.1.11 - # via -r dev-requirements.in -flake8-comprehensions==3.8.0 - # via -r dev-requirements.in -flake8-polyfill==1.0.2 - # via pep8-naming -ghp-import==2.0.2 - # via mkdocs -griffe==0.12.6 - # via mkdocstrings-python -idna==3.3 - # via requests -importlib-metadata==4.11.1 - # via - # markdown - # mkdocs -iniconfig==1.1.1 - # via pytest -isort==5.10.1 - # via -r dev-requirements.in -jinja2==3.0.3 - # via - # mkdocs - # mkdocs-material - # mkdocstrings -lxml==4.8.0 - # via mkdocs-htmlproofer-plugin -markdown==3.3.6 - # via - # mkdocs - # mkdocs-autorefs - # mkdocs-htmlproofer-plugin - # mkdocs-material - # mkdocstrings - # pymdown-extensions -markupsafe==2.1.0 - # via - # jinja2 - # mkdocstrings -mccabe==0.6.1 - # via flake8 -mergedeep==1.3.4 - # via mkdocs -mkdocs==1.2.3 - # via - # mkdocs-autorefs - # mkdocs-gen-files - # mkdocs-htmlproofer-plugin - # mkdocs-literate-nav - # mkdocs-material - # mkdocstrings -mkdocs-autorefs==0.3.1 - # via mkdocstrings -mkdocs-gen-files==0.3.4 - # via -r dev-requirements.in -mkdocs-htmlproofer-plugin==0.8.0 - # via -r dev-requirements.in -mkdocs-literate-nav==0.4.1 - # via -r dev-requirements.in -mkdocs-material==8.2.1 - # via -r dev-requirements.in -mkdocs-material-extensions==1.0.3 - # via mkdocs-material -mkdocstrings[python]==0.18.0 - # via - # -r dev-requirements.in - # mkdocstrings-python - # mkdocstrings-python-legacy -mkdocstrings-python==0.6.3 - # via mkdocstrings -mkdocstrings-python-legacy==0.2.2 - # via mkdocstrings -mypy==0.931 - # via -r dev-requirements.in -mypy-extensions==0.4.3 - # via - # black - # mypy -packaging==21.3 - # via - # mkdocs - # pytest - # tox -pathspec==0.9.0 - # via black -pep8-naming==0.12.1 - # via -r dev-requirements.in -platformdirs==2.5.1 - # via - # black - # virtualenv -pluggy==1.0.0 - # via - # pytest - # tox -py==1.11.0 - # via - # pytest - # tox -pycodestyle==2.8.0 - # via flake8 -pyflakes==2.4.0 - # via flake8 -pygments==2.11.2 - # via mkdocs-material -pymdown-extensions==9.2 - # via - # mkdocs-material - # mkdocstrings -pyparsing==3.0.7 - # via packaging -pytest==7.0.1 - # via - # -r dev-requirements.in - # pytest-cov -pytest-cov==3.0.0 - # via -r dev-requirements.in -python-dateutil==2.8.2 - # via ghp-import -pytkdocs==0.16.0 - # via mkdocstrings-python-legacy -pyyaml==6.0 - # via - # mkdocs - # pyyaml-env-tag -pyyaml-env-tag==0.1 - # via mkdocs -requests==2.27.1 - # via mkdocs-htmlproofer-plugin -six==1.16.0 - # via - # python-dateutil - # tox - # virtualenv -soupsieve==2.3.1 - # via beautifulsoup4 -toml==0.10.2 - # via tox -tomli==2.0.1 - # via - # black - # coverage - # mypy - # pytest -tox==3.24.5 - # via -r dev-requirements.in -typing-extensions==4.1.1 - # via - # black - # mypy -urllib3==1.26.8 - # via requests -virtualenv==20.13.1 - # via tox -watchdog==2.1.6 - # via mkdocs -zipp==3.7.0 - # via importlib-metadata diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000..684103a --- /dev/null +++ b/noxfile.py @@ -0,0 +1,98 @@ +import nox +from nox_poetry import Session, session + +nox.options.reuse_existing_virtualenvs = True +nox.options.sessions = ["fmt_check", "type_check", "lint", "test", "docs"] + +test_dependencies = ["pytest", "pytest-cov"] + + +@session(python=["3.8", "3.9", "3.10"]) +def test(s: Session) -> None: + s.install(".", *test_dependencies) + s.run( + "python", + "-m", + "pytest", + "--cov", + "fact", + "--cov-report", + "html", + "--cov-report", + "term", + *s.posargs, + ) + + +@session +def type_check(s: Session) -> None: + # It is important to install the main project and test dependencies, as some packages contain + # inline type hints (PEP 561) that mypy will use. + s.install(".", *test_dependencies, "mypy") + s.run("mypy", "src", "tests") + + +@session +def lint(s: Session) -> None: + s.install( + "flake8", + "flake8-bugbear", + "flake8-broken-line", + "flake8-comprehensions", + "pep8-naming", + "pyproject-flake8", + ) + s.run("pflake8") + + +fmt_dependencies = ["isort", "black"] + + +@session +def fmt(s: Session) -> None: + s.install(*fmt_dependencies) + s.run("isort", ".") + s.run("black", ".") + + +@session +def fmt_check(s: Session) -> None: + s.install(*fmt_dependencies) + s.run("isort", "--check", ".") + s.run("black", "--check", ".") + + +doc_dependencies = [ + "mkdocs-material", + "mkdocs-htmlproofer-plugin", + "mkdocstrings[python]", + "mkdocs-gen-files", + "mkdocs-literate-nav", +] + +# Environment variable needed for mkdocstrings-python to locate source files. +doc_env = {"PYTHONPATH": "src"} + + +@session +def docs(s: Session) -> None: + s.install(*doc_dependencies) + s.run("mkdocs", "build", env=doc_env) + + +@session +def docs_serve(s: Session) -> None: + s.install(*doc_dependencies) + s.run("mkdocs", "serve", env=doc_env) + + +@session +def docs_github_pages(s: Session) -> None: + s.install(*doc_dependencies) + s.run("mkdocs", "gh-deploy", "--force", env=doc_env) + + +@session(reuse_venv=False) +def licenses(s: Session) -> None: + s.install(".", "pip-licenses") + s.run("pip-licenses", *s.posargs) diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..b302431 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,1428 @@ +[[package]] +name = "argcomplete" +version = "1.12.3" +description = "Bash tab completion for argparse" +category = "dev" +optional = false +python-versions = "*" + +[package.extras] +test = ["coverage", "flake8", "pexpect", "wheel"] + +[[package]] +name = "astunparse" +version = "1.6.3" +description = "An AST unparser for Python" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +six = ">=1.6.1,<2.0" + +[[package]] +name = "atomicwrites" +version = "1.4.0" +description = "Atomic file writes." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "attrs" +version = "21.4.0" +description = "Classes Without Boilerplate" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "beautifulsoup4" +version = "4.11.1" +description = "Screen-scraping library" +category = "dev" +optional = false +python-versions = ">=3.6.0" + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "black" +version = "22.3.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + +[[package]] +name = "certifi" +version = "2021.10.8" +description = "Python package for providing Mozilla's CA Bundle." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "charset-normalizer" +version = "2.0.12" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "dev" +optional = false +python-versions = ">=3.5.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.4" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "colorlog" +version = "6.6.0" +description = "Add colours to the output of Python's logging module." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +colorama = {version = "*", markers = "sys_platform == \"win32\""} + +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + +[[package]] +name = "coverage" +version = "6.3.3" +description = "Code coverage measurement for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +tomli = {version = "*", optional = true, markers = "extra == \"toml\""} + +[package.extras] +toml = ["tomli"] + +[[package]] +name = "distlib" +version = "0.3.4" +description = "Distribution utilities" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "filelock" +version = "3.7.0" +description = "A platform independent file lock." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] +testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] + +[[package]] +name = "flake8" +version = "4.0.1" +description = "the modular source code checker: pep8 pyflakes and co" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mccabe = ">=0.6.0,<0.7.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" + +[[package]] +name = "flake8-broken-line" +version = "0.4.0" +description = "Flake8 plugin to forbid backslashes for line breaks" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +flake8 = ">=3.5,<5" + +[[package]] +name = "flake8-bugbear" +version = "22.4.25" +description = "A plugin for flake8 finding likely bugs and design problems in your program. Contains warnings that don't belong in pyflakes and pycodestyle." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +attrs = ">=19.2.0" +flake8 = ">=3.0.0" + +[package.extras] +dev = ["coverage", "hypothesis", "hypothesmith (>=0.2)", "pre-commit"] + +[[package]] +name = "flake8-comprehensions" +version = "3.9.0" +description = "A flake8 plugin to help you write better list/set/dict comprehensions." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +flake8 = ">=3.0,<3.2.0 || >3.2.0" + +[[package]] +name = "flake8-polyfill" +version = "1.0.2" +description = "Polyfill package for Flake8 plugins" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" + +[[package]] +name = "ghp-import" +version = "2.1.0" +description = "Copy your docs directly to the gh-pages branch." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["twine", "markdown", "flake8", "wheel"] + +[[package]] +name = "griffe" +version = "0.19.1" +description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +async = ["aiofiles (>=0.7,<1.0)"] + +[[package]] +name = "idna" +version = "3.3" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "4.11.3" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "iniconfig" +version = "1.1.1" +description = "iniconfig: brain-dead simple config-ini parsing" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "lxml" +version = "4.8.0" +description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" + +[package.extras] +cssselect = ["cssselect (>=0.7)"] +html5 = ["html5lib"] +htmlsoup = ["beautifulsoup4"] +source = ["Cython (>=0.29.7)"] + +[[package]] +name = "markdown" +version = "3.3.7" +description = "Python implementation of Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} + +[package.extras] +testing = ["coverage", "pyyaml"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "mccabe" +version = "0.6.1" +description = "McCabe checker, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mkdocs" +version = "1.3.0" +description = "Project documentation with Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=4.3" +Jinja2 = ">=2.10.2" +Markdown = ">=3.2.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" +PyYAML = ">=3.10" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] + +[[package]] +name = "mkdocs-autorefs" +version = "0.4.1" +description = "Automatically link across pages in MkDocs." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +Markdown = ">=3.3" +mkdocs = ">=1.1" + +[[package]] +name = "mkdocs-gen-files" +version = "0.3.4" +description = "MkDocs plugin to programmatically generate documentation pages during the build" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +mkdocs = ">=1.0.3,<2.0.0" + +[[package]] +name = "mkdocs-htmlproofer-plugin" +version = "0.8.0" +description = "A MkDocs plugin that validates URL in rendered HTML files" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +beautifulsoup4 = "*" +lxml = "*" +Markdown = "*" +mkdocs = ">=0.17" +requests = "*" + +[[package]] +name = "mkdocs-literate-nav" +version = "0.4.1" +description = "MkDocs plugin to specify the navigation in Markdown instead of YAML" +category = "dev" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +mkdocs = ">=1.0.3,<2.0.0" + +[[package]] +name = "mkdocs-material" +version = "8.2.15" +description = "Documentation that simply works" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +jinja2 = ">=2.11.1" +markdown = ">=3.2" +mkdocs = ">=1.3.0" +mkdocs-material-extensions = ">=1.0.3" +pygments = ">=2.12" +pymdown-extensions = ">=9.4" + +[[package]] +name = "mkdocs-material-extensions" +version = "1.0.3" +description = "Extension pack for Python Markdown." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "mkdocstrings" +version = "0.18.1" +description = "Automatic documentation from sources, for MkDocs." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +Jinja2 = ">=2.11.1" +Markdown = ">=3.3" +MarkupSafe = ">=1.1" +mkdocs = ">=1.2" +mkdocs-autorefs = ">=0.3.1" +mkdocstrings-python = {version = ">=0.5.2", optional = true, markers = "extra == \"python\""} +mkdocstrings-python-legacy = ">=0.2" +pymdown-extensions = ">=6.3" + +[package.extras] +crystal = ["mkdocstrings-crystal (>=0.3.4)"] +python = ["mkdocstrings-python (>=0.5.2)"] +python-legacy = ["mkdocstrings-python-legacy (>=0.2.1)"] + +[[package]] +name = "mkdocstrings-python" +version = "0.6.6" +description = "A Python handler for mkdocstrings." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +griffe = ">=0.11.1" +mkdocstrings = ">=0.18" + +[[package]] +name = "mkdocstrings-python-legacy" +version = "0.2.2" +description = "A legacy Python handler for mkdocstrings." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +mkdocstrings = ">=0.18" +pytkdocs = ">=0.14" + +[[package]] +name = "mypy" +version = "0.950" +description = "Optional static typing for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = ">=3.10" + +[package.extras] +dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<2)"] +reports = ["lxml"] + +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "nox" +version = "2022.1.7" +description = "Flexible test automation." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +argcomplete = ">=1.9.4,<2.0" +colorlog = ">=2.6.1,<7.0.0" +packaging = ">=20.9" +py = ">=1.4.0,<2.0.0" +virtualenv = ">=14.0.0" + +[package.extras] +tox_to_nox = ["jinja2", "tox"] + +[[package]] +name = "nox-poetry" +version = "0.9.0" +description = "nox-poetry" +category = "dev" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +nox = ">=2020.8.22" +packaging = ">=20.9" +tomlkit = ">=0.7.0,<0.8.0" + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[[package]] +name = "pep8-naming" +version = "0.12.1" +description = "Check PEP-8 naming conventions, plugin for flake8" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = ">=3.9.1" +flake8-polyfill = ">=1.0.2,<2" + +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +dev = ["pre-commit", "tox"] +testing = ["pytest", "pytest-benchmark"] + +[[package]] +name = "py" +version = "1.11.0" +description = "library with cross-python path, ini-parsing, io, code, log facilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pycodestyle" +version = "2.8.0" +description = "Python style guide checker" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "pyflakes" +version = "2.4.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[[package]] +name = "pygments" +version = "2.12.0" +description = "Pygments is a syntax highlighting package written in Python." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pymdown-extensions" +version = "9.4" +description = "Extension pack for Python Markdown." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +markdown = ">=3.2" + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "dev" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "pyproject-flake8" +version = "0.0.1a4" +description = "pyproject-flake8 (`pflake8`), a monkey patching wrapper to connect flake8 with pyproject.toml configuration" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +flake8 = "*" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[[package]] +name = "pytest" +version = "7.1.2" +description = "pytest: simple powerful testing with Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} +attrs = ">=19.2.0" +colorama = {version = "*", markers = "sys_platform == \"win32\""} +iniconfig = "*" +packaging = "*" +pluggy = ">=0.12,<2.0" +py = ">=1.8.2" +tomli = ">=1.0.0" + +[package.extras] +testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] + +[[package]] +name = "pytest-cov" +version = "3.0.0" +description = "Pytest plugin for measuring coverage." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +coverage = {version = ">=5.2.1", extras = ["toml"]} +pytest = ">=4.6" + +[package.extras] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] + +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + +[[package]] +name = "pytkdocs" +version = "0.16.1" +description = "Load Python objects documentation." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +astunparse = {version = ">=1.6", markers = "python_version < \"3.9\""} + +[package.extras] +numpy-style = ["docstring_parser (>=0.7)"] + +[[package]] +name = "pyyaml" +version = "6.0" +description = "YAML parser and emitter for Python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyyaml = "*" + +[[package]] +name = "requests" +version = "2.27.1" +description = "Python HTTP for Humans." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} +idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "soupsieve" +version = "2.3.2.post1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tomlkit" +version = "0.7.2" +description = "Style preserving TOML library" +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "typer" +version = "0.4.1" +description = "Typer, build great CLIs. Easy to code. Based on Python type hints." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +click = ">=7.1.1,<9.0.0" + +[package.extras] +all = ["colorama (>=0.4.3,<0.5.0)", "shellingham (>=1.3.0,<2.0.0)"] +dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] +doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)"] +test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)"] + +[[package]] +name = "typing-extensions" +version = "4.2.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "urllib3" +version = "1.26.9" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "dev" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "virtualenv" +version = "20.14.1" +description = "Virtual Python Environment builder" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + +[package.dependencies] +distlib = ">=0.3.1,<1" +filelock = ">=3.2,<4" +platformdirs = ">=2,<3" +six = ">=1.9.0,<2" + +[package.extras] +docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] +testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] + +[[package]] +name = "watchdog" +version = "2.1.7" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + +[[package]] +name = "zipp" +version = "3.8.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = "^3.8" +content-hash = "c72015b08cfc473ec9e6ec466c6ee74d8ce0a1c34a1b32f080b78b61e90fa841" + +[metadata.files] +argcomplete = [ + {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, + {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, +] +astunparse = [ + {file = "astunparse-1.6.3-py2.py3-none-any.whl", hash = "sha256:c2652417f2c8b5bb325c885ae329bdf3f86424075c4fd1a128674bc6fba4b8e8"}, + {file = "astunparse-1.6.3.tar.gz", hash = "sha256:5ad93a8456f0d084c3456d059fd9a92cce667963232cbf763eac3bc5b7940872"}, +] +atomicwrites = [ + {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, + {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, +] +attrs = [ + {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, + {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, +] +beautifulsoup4 = [ + {file = "beautifulsoup4-4.11.1-py3-none-any.whl", hash = "sha256:58d5c3d29f5a36ffeb94f02f0d786cd53014cf9b3b3951d42e0080d8a9498d30"}, + {file = "beautifulsoup4-4.11.1.tar.gz", hash = "sha256:ad9aa55b65ef2808eb405f46cf74df7fcb7044d5cbc26487f96eb2ef2e436693"}, +] +black = [ + {file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"}, + {file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"}, + {file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"}, + {file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"}, + {file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"}, + {file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"}, + {file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"}, + {file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"}, + {file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"}, + {file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"}, + {file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"}, + {file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"}, + {file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"}, + {file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"}, + {file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"}, + {file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"}, + {file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"}, + {file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"}, + {file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"}, + {file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"}, + {file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"}, +] +certifi = [ + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, +] +charset-normalizer = [ + {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, + {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] +colorama = [ + {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, + {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, +] +colorlog = [ + {file = "colorlog-6.6.0-py2.py3-none-any.whl", hash = "sha256:351c51e866c86c3217f08e4b067a7974a678be78f07f85fc2d55b8babde6d94e"}, + {file = "colorlog-6.6.0.tar.gz", hash = "sha256:344f73204009e4c83c5b6beb00b3c45dc70fcdae3c80db919e0a4171d006fde8"}, +] +coverage = [ + {file = "coverage-6.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:df32ee0f4935a101e4b9a5f07b617d884a531ed5666671ff6ac66d2e8e8246d8"}, + {file = "coverage-6.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:75b5dbffc334e0beb4f6c503fb95e6d422770fd2d1b40a64898ea26d6c02742d"}, + {file = "coverage-6.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:114944e6061b68a801c5da5427b9173a0dd9d32cd5fcc18a13de90352843737d"}, + {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ab88a01cd180b5640ccc9c47232e31924d5f9967ab7edd7e5c91c68eee47a69"}, + {file = "coverage-6.3.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad8f9068f5972a46d50fe5f32c09d6ee11da69c560fcb1b4c3baea246ca4109b"}, + {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:4cd696aa712e6cd16898d63cf66139dc70d998f8121ab558f0e1936396dbc579"}, + {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:c1a9942e282cc9d3ed522cd3e3cab081149b27ea3bda72d6f61f84eaf88c1a63"}, + {file = "coverage-6.3.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c06455121a089252b5943ea682187a4e0a5cf0a3fb980eb8e7ce394b144430a9"}, + {file = "coverage-6.3.3-cp310-cp310-win32.whl", hash = "sha256:cb5311d6ccbd22578c80028c5e292a7ab9adb91bd62c1982087fad75abe2e63d"}, + {file = "coverage-6.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:6d4a6f30f611e657495cc81a07ff7aa8cd949144e7667c5d3e680d73ba7a70e4"}, + {file = "coverage-6.3.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:79bf405432428e989cad7b8bc60581963238f7645ae8a404f5dce90236cc0293"}, + {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:338c417613f15596af9eb7a39353b60abec9d8ce1080aedba5ecee6a5d85f8d3"}, + {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db094a6a4ae6329ed322a8973f83630b12715654c197dd392410400a5bfa1a73"}, + {file = "coverage-6.3.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1414e8b124611bf4df8d77215bd32cba6e3425da8ce9c1f1046149615e3a9a31"}, + {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:93b16b08f94c92cab88073ffd185070cdcb29f1b98df8b28e6649145b7f2c90d"}, + {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fbc86ae8cc129c801e7baaafe3addf3c8d49c9c1597c44bdf2d78139707c3c62"}, + {file = "coverage-6.3.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b5ba058610e8289a07db2a57bce45a1793ec0d3d11db28c047aae2aa1a832572"}, + {file = "coverage-6.3.3-cp37-cp37m-win32.whl", hash = "sha256:8329635c0781927a2c6ae068461e19674c564e05b86736ab8eb29c420ee7dc20"}, + {file = "coverage-6.3.3-cp37-cp37m-win_amd64.whl", hash = "sha256:e5af1feee71099ae2e3b086ec04f57f9950e1be9ecf6c420696fea7977b84738"}, + {file = "coverage-6.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e814a4a5a1d95223b08cdb0f4f57029e8eab22ffdbae2f97107aeef28554517e"}, + {file = "coverage-6.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:61f4fbf3633cb0713437291b8848634ea97f89c7e849c2be17a665611e433f53"}, + {file = "coverage-6.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3401b0d2ed9f726fadbfa35102e00d1b3547b73772a1de5508ef3bdbcb36afe7"}, + {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8586b177b4407f988731eb7f41967415b2197f35e2a6ee1a9b9b561f6323c8e9"}, + {file = "coverage-6.3.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:892e7fe32191960da559a14536768a62e83e87bbb867e1b9c643e7e0fbce2579"}, + {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:afb03f981fadb5aed1ac6e3dd34f0488e1a0875623d557b6fad09b97a942b38a"}, + {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:cbe91bc84be4e5ef0b1480d15c7b18e29c73bdfa33e07d3725da7d18e1b0aff2"}, + {file = "coverage-6.3.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:91502bf27cbd5c83c95cfea291ef387469f2387508645602e1ca0fd8a4ba7548"}, + {file = "coverage-6.3.3-cp38-cp38-win32.whl", hash = "sha256:c488db059848702aff30aa1d90ef87928d4e72e4f00717343800546fdbff0a94"}, + {file = "coverage-6.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6534fcdfb5c503affb6b1130db7b5bfc8a0f77fa34880146f7a5c117987d0"}, + {file = "coverage-6.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cc692c9ee18f0dd3214843779ba6b275ee4bb9b9a5745ba64265bce911aefd1a"}, + {file = "coverage-6.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:462105283de203df8de58a68c1bb4ba2a8a164097c2379f664fa81d6baf94b81"}, + {file = "coverage-6.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc972d829ad5ef4d4c5fcabd2bbe2add84ce8236f64ba1c0c72185da3a273130"}, + {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06f54765cdbce99901871d50fe9f41d58213f18e98b170a30ca34f47de7dd5e8"}, + {file = "coverage-6.3.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7835f76a081787f0ca62a53504361b3869840a1620049b56d803a8cb3a9eeea3"}, + {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6f5fee77ec3384b934797f1873758f796dfb4f167e1296dc00f8b2e023ce6ee9"}, + {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:baa8be8aba3dd1e976e68677be68a960a633a6d44c325757aefaa4d66175050f"}, + {file = "coverage-6.3.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4d06380e777dd6b35ee936f333d55b53dc4a8271036ff884c909cf6e94be8b6c"}, + {file = "coverage-6.3.3-cp39-cp39-win32.whl", hash = "sha256:f8cabc5fd0091976ab7b020f5708335033e422de25e20ddf9416bdce2b7e07d8"}, + {file = "coverage-6.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c9441d57b0963cf8340268ad62fc83de61f1613034b79c2b1053046af0c5284"}, + {file = "coverage-6.3.3-pp36.pp37.pp38-none-any.whl", hash = "sha256:d522f1dc49127eab0bfbba4e90fa068ecff0899bbf61bf4065c790ddd6c177fe"}, + {file = "coverage-6.3.3.tar.gz", hash = "sha256:2781c43bffbbec2b8867376d4d61916f5e9c4cc168232528562a61d1b4b01879"}, +] +distlib = [ + {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, + {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, +] +filelock = [ + {file = "filelock-3.7.0-py3-none-any.whl", hash = "sha256:c7b5fdb219b398a5b28c8e4c1893ef5f98ece6a38c6ab2c22e26ec161556fed6"}, + {file = "filelock-3.7.0.tar.gz", hash = "sha256:b795f1b42a61bbf8ec7113c341dad679d772567b936fbd1bf43c9a238e673e20"}, +] +flake8 = [ + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, +] +flake8-broken-line = [ + {file = "flake8-broken-line-0.4.0.tar.gz", hash = "sha256:771aab5aa0997666796fed249d0e48e6c01cdfeca8c95521eea28a38b7ced4c7"}, + {file = "flake8_broken_line-0.4.0-py3-none-any.whl", hash = "sha256:e9c522856862239a2c7ef2c1de0276fa598572aa864bd4e9c7efc2a827538515"}, +] +flake8-bugbear = [ + {file = "flake8-bugbear-22.4.25.tar.gz", hash = "sha256:f7c080563fca75ee6b205d06b181ecba22b802babb96b0b084cc7743d6908a55"}, + {file = "flake8_bugbear-22.4.25-py3-none-any.whl", hash = "sha256:ec374101cddf65bd7a96d393847d74e58d3b98669dbf9768344c39b6290e8bd6"}, +] +flake8-comprehensions = [ + {file = "flake8-comprehensions-3.9.0.tar.gz", hash = "sha256:20c2f6846090e8f265e757f0aa500614c88b0f9ffb0d954d330dcd8abd8f874e"}, + {file = "flake8_comprehensions-3.9.0-py3-none-any.whl", hash = "sha256:ef3a069809d4eb60805cd9b0ea2aa1aae21b527a460e704c01e0dd3416c99aeb"}, +] +flake8-polyfill = [ + {file = "flake8-polyfill-1.0.2.tar.gz", hash = "sha256:e44b087597f6da52ec6393a709e7108b2905317d0c0b744cdca6208e670d8eda"}, + {file = "flake8_polyfill-1.0.2-py2.py3-none-any.whl", hash = "sha256:12be6a34ee3ab795b19ca73505e7b55826d5f6ad7230d31b18e106400169b9e9"}, +] +ghp-import = [ + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, +] +griffe = [ + {file = "griffe-0.19.1-py3-none-any.whl", hash = "sha256:7d9393f8d6d6026d80dc0865724b9d87f2cbb93e3f86998b785b01403cd3d52b"}, + {file = "griffe-0.19.1.tar.gz", hash = "sha256:945284500df94f272b8a70bcedaac99721f26dea72d2f1b16f416de28848b753"}, +] +idna = [ + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, +] +importlib-metadata = [ + {file = "importlib_metadata-4.11.3-py3-none-any.whl", hash = "sha256:1208431ca90a8cca1a6b8af391bb53c1a2db74e5d1cef6ddced95d4b2062edc6"}, + {file = "importlib_metadata-4.11.3.tar.gz", hash = "sha256:ea4c597ebf37142f827b8f39299579e31685c31d3a438b59f469406afd0f2539"}, +] +iniconfig = [ + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, +] +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, +] +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, +] +lxml = [ + {file = "lxml-4.8.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e1ab2fac607842ac36864e358c42feb0960ae62c34aa4caaf12ada0a1fb5d99b"}, + {file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28d1af847786f68bec57961f31221125c29d6f52d9187c01cd34dc14e2b29430"}, + {file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b92d40121dcbd74831b690a75533da703750f7041b4bf951befc657c37e5695a"}, + {file = "lxml-4.8.0-cp27-cp27m-win32.whl", hash = "sha256:e01f9531ba5420838c801c21c1b0f45dbc9607cb22ea2cf132844453bec863a5"}, + {file = "lxml-4.8.0-cp27-cp27m-win_amd64.whl", hash = "sha256:6259b511b0f2527e6d55ad87acc1c07b3cbffc3d5e050d7e7bcfa151b8202df9"}, + {file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1010042bfcac2b2dc6098260a2ed022968dbdfaf285fc65a3acf8e4eb1ffd1bc"}, + {file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa56bb08b3dd8eac3a8c5b7d075c94e74f755fd9d8a04543ae8d37b1612dd170"}, + {file = "lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:31ba2cbc64516dcdd6c24418daa7abff989ddf3ba6d3ea6f6ce6f2ed6e754ec9"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:31499847fc5f73ee17dbe1b8e24c6dafc4e8d5b48803d17d22988976b0171f03"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:5f7d7d9afc7b293147e2d506a4596641d60181a35279ef3aa5778d0d9d9123fe"}, + {file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a3c5f1a719aa11866ffc530d54ad965063a8cbbecae6515acbd5f0fae8f48eaa"}, + {file = "lxml-4.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6268e27873a3d191849204d00d03f65c0e343b3bcb518a6eaae05677c95621d1"}, + {file = "lxml-4.8.0-cp310-cp310-win32.whl", hash = "sha256:330bff92c26d4aee79c5bc4d9967858bdbe73fdbdbacb5daf623a03a914fe05b"}, + {file = "lxml-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2582b238e1658c4061ebe1b4df53c435190d22457642377fd0cb30685cdfb76"}, + {file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2bfc7e2a0601b475477c954bf167dee6d0f55cb167e3f3e7cefad906e7759f6"}, + {file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1547ff4b8a833511eeaceacbcd17b043214fcdb385148f9c1bc5556ca9623e2"}, + {file = "lxml-4.8.0-cp35-cp35m-win32.whl", hash = "sha256:a9f1c3489736ff8e1c7652e9dc39f80cff820f23624f23d9eab6e122ac99b150"}, + {file = "lxml-4.8.0-cp35-cp35m-win_amd64.whl", hash = "sha256:530f278849031b0eb12f46cca0e5db01cfe5177ab13bd6878c6e739319bae654"}, + {file = "lxml-4.8.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:078306d19a33920004addeb5f4630781aaeabb6a8d01398045fcde085091a169"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:86545e351e879d0b72b620db6a3b96346921fa87b3d366d6c074e5a9a0b8dadb"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24f5c5ae618395ed871b3d8ebfcbb36e3f1091fd847bf54c4de623f9107942f3"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bbab6faf6568484707acc052f4dfc3802bdb0cafe079383fbaa23f1cdae9ecd4"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7993232bd4044392c47779a3c7e8889fea6883be46281d45a81451acfd704d7e"}, + {file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d6483b1229470e1d8835e52e0ff3c6973b9b97b24cd1c116dca90b57a2cc613"}, + {file = "lxml-4.8.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ad4332a532e2d5acb231a2e5d33f943750091ee435daffca3fec0a53224e7e33"}, + {file = "lxml-4.8.0-cp36-cp36m-win32.whl", hash = "sha256:db3535733f59e5605a88a706824dfcb9bd06725e709ecb017e165fc1d6e7d429"}, + {file = "lxml-4.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5f148b0c6133fb928503cfcdfdba395010f997aa44bcf6474fcdd0c5398d9b63"}, + {file = "lxml-4.8.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8a31f24e2a0b6317f33aafbb2f0895c0bce772980ae60c2c640d82caac49628a"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:719544565c2937c21a6f76d520e6e52b726d132815adb3447ccffbe9f44203c4"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c0b88ed1ae66777a798dc54f627e32d3b81c8009967c63993c450ee4cbcbec15"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fa9b7c450be85bfc6cd39f6df8c5b8cbd76b5d6fc1f69efec80203f9894b885f"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9f84ed9f4d50b74fbc77298ee5c870f67cb7e91dcdc1a6915cb1ff6a317476c"}, + {file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1d650812b52d98679ed6c6b3b55cbb8fe5a5460a0aef29aeb08dc0b44577df85"}, + {file = "lxml-4.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:80bbaddf2baab7e6de4bc47405e34948e694a9efe0861c61cdc23aa774fcb141"}, + {file = "lxml-4.8.0-cp37-cp37m-win32.whl", hash = "sha256:6f7b82934c08e28a2d537d870293236b1000d94d0b4583825ab9649aef7ddf63"}, + {file = "lxml-4.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e1fd7d2fe11f1cb63d3336d147c852f6d07de0d0020d704c6031b46a30b02ca8"}, + {file = "lxml-4.8.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5045ee1ccd45a89c4daec1160217d363fcd23811e26734688007c26f28c9e9e7"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0c1978ff1fd81ed9dcbba4f91cf09faf1f8082c9d72eb122e92294716c605428"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cbf2ff155b19dc4d4100f7442f6a697938bf4493f8d3b0c51d45568d5666b5"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ce13d6291a5f47c1c8dbd375baa78551053bc6b5e5c0e9bb8e39c0a8359fd52f"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11527dc23d5ef44d76fef11213215c34f36af1608074561fcc561d983aeb870"}, + {file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:60d2f60bd5a2a979df28ab309352cdcf8181bda0cca4529769a945f09aba06f9"}, + {file = "lxml-4.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:62f93eac69ec0f4be98d1b96f4d6b964855b8255c345c17ff12c20b93f247b68"}, + {file = "lxml-4.8.0-cp38-cp38-win32.whl", hash = "sha256:20b8a746a026017acf07da39fdb10aa80ad9877046c9182442bf80c84a1c4696"}, + {file = "lxml-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:891dc8f522d7059ff0024cd3ae79fd224752676447f9c678f2a5c14b84d9a939"}, + {file = "lxml-4.8.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b6fc2e2fb6f532cf48b5fed57567ef286addcef38c28874458a41b7837a57807"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:74eb65ec61e3c7c019d7169387d1b6ffcfea1b9ec5894d116a9a903636e4a0b1"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:627e79894770783c129cc5e89b947e52aa26e8e0557c7e205368a809da4b7939"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:545bd39c9481f2e3f2727c78c169425efbfb3fbba6e7db4f46a80ebb249819ca"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5a58d0b12f5053e270510bf12f753a76aaf3d74c453c00942ed7d2c804ca845c"}, + {file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec4b4e75fc68da9dc0ed73dcdb431c25c57775383fec325d23a770a64e7ebc87"}, + {file = "lxml-4.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5804e04feb4e61babf3911c2a974a5b86f66ee227cc5006230b00ac6d285b3a9"}, + {file = "lxml-4.8.0-cp39-cp39-win32.whl", hash = "sha256:aa0cf4922da7a3c905d000b35065df6184c0dc1d866dd3b86fd961905bbad2ea"}, + {file = "lxml-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:dd10383f1d6b7edf247d0960a3db274c07e96cf3a3fc7c41c8448f93eac3fb1c"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2403a6d6fb61c285969b71f4a3527873fe93fd0abe0832d858a17fe68c8fa507"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:986b7a96228c9b4942ec420eff37556c5777bfba6758edcb95421e4a614b57f9"}, + {file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6fe4ef4402df0250b75ba876c3795510d782def5c1e63890bde02d622570d39e"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:f10ce66fcdeb3543df51d423ede7e238be98412232fca5daec3e54bcd16b8da0"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:730766072fd5dcb219dd2b95c4c49752a54f00157f322bc6d71f7d2a31fecd79"}, + {file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8b99ec73073b37f9ebe8caf399001848fced9c08064effdbfc4da2b5a8d07b93"}, + {file = "lxml-4.8.0.tar.gz", hash = "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23"}, +] +markdown = [ + {file = "Markdown-3.3.7-py3-none-any.whl", hash = "sha256:f5da449a6e1c989a4cea2631aa8ee67caa5a2ef855d551c88f9e309f4634c621"}, + {file = "Markdown-3.3.7.tar.gz", hash = "sha256:cbb516f16218e643d8e0a95b309f77eb118cb138d39a4f27851e6a63581db874"}, +] +markupsafe = [ + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, +] +mccabe = [ + {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, + {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, +] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] +mkdocs = [ + {file = "mkdocs-1.3.0-py3-none-any.whl", hash = "sha256:26bd2b03d739ac57a3e6eed0b7bcc86168703b719c27b99ad6ca91dc439aacde"}, + {file = "mkdocs-1.3.0.tar.gz", hash = "sha256:b504405b04da38795fec9b2e5e28f6aa3a73bb0960cb6d5d27ead28952bd35ea"}, +] +mkdocs-autorefs = [ + {file = "mkdocs-autorefs-0.4.1.tar.gz", hash = "sha256:70748a7bd025f9ecd6d6feeba8ba63f8e891a1af55f48e366d6d6e78493aba84"}, + {file = "mkdocs_autorefs-0.4.1-py3-none-any.whl", hash = "sha256:a2248a9501b29dc0cc8ba4c09f4f47ff121945f6ce33d760f145d6f89d313f5b"}, +] +mkdocs-gen-files = [ + {file = "mkdocs-gen-files-0.3.4.tar.gz", hash = "sha256:c69188486bdc1e74bd2b9b7ebbde9f9eb21052ae7762f1b35420cfbfc6d7122e"}, + {file = "mkdocs_gen_files-0.3.4-py3-none-any.whl", hash = "sha256:07f43245c87a03cfb03884e767655c2a61def24d07e47fb3a8d26b1581524d6a"}, +] +mkdocs-htmlproofer-plugin = [ + {file = "mkdocs-htmlproofer-plugin-0.8.0.tar.gz", hash = "sha256:48db49ede2d94939b4773feaa8159f88803bf5edd87ea99c78665f8d61107651"}, + {file = "mkdocs_htmlproofer_plugin-0.8.0-py3-none-any.whl", hash = "sha256:914fa007df4182e22440c5d23670eba36392e7f58f99de728f3b1d7cfb4847c6"}, +] +mkdocs-literate-nav = [ + {file = "mkdocs-literate-nav-0.4.1.tar.gz", hash = "sha256:9efe26b662f2f901cae5807bfd51446d30ea7e033c2bc43a15d6282c7dfac1ab"}, + {file = "mkdocs_literate_nav-0.4.1-py3-none-any.whl", hash = "sha256:a4b761792ba21defbe2dfd5e0de6ba451639e1ca0f0661c37eda83cc6261e4f9"}, +] +mkdocs-material = [ + {file = "mkdocs-material-8.2.15.tar.gz", hash = "sha256:93b57e53733051431cc83216446e774bdf08bf516a6251ff2f24974f45f98149"}, + {file = "mkdocs_material-8.2.15-py2.py3-none-any.whl", hash = "sha256:9d6c4ca1ceecc00b2e38c214665ed7605d275321dcaa22f38b9d1175edc58955"}, +] +mkdocs-material-extensions = [ + {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, + {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, +] +mkdocstrings = [ + {file = "mkdocstrings-0.18.1-py3-none-any.whl", hash = "sha256:4053929356df8cd69ed32eef71d8f676a472ef72980c9ffd4f933ead1debcdad"}, + {file = "mkdocstrings-0.18.1.tar.gz", hash = "sha256:fb7c91ce7e3ab70488d3fa6c073a4f827cdc319042f682ef8ea95459790d64fc"}, +] +mkdocstrings-python = [ + {file = "mkdocstrings-python-0.6.6.tar.gz", hash = "sha256:37281696b9f199624ae420e0625b6659b7fdfbea736618bce7fd978682dea3b1"}, + {file = "mkdocstrings_python-0.6.6-py3-none-any.whl", hash = "sha256:c118438d3cb4b14c492a51d109f4e5b27ab06ba19b099d624430dfd904926152"}, +] +mkdocstrings-python-legacy = [ + {file = "mkdocstrings-python-legacy-0.2.2.tar.gz", hash = "sha256:f0e7ec6a19750581b752acb38f6b32fcd1efe006f14f6703125d2c2c9a5c6f02"}, + {file = "mkdocstrings_python_legacy-0.2.2-py3-none-any.whl", hash = "sha256:379107a3a5b8db9b462efc4493c122efe21e825e3702425dbd404621302a563a"}, +] +mypy = [ + {file = "mypy-0.950-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cf9c261958a769a3bd38c3e133801ebcd284ffb734ea12d01457cb09eacf7d7b"}, + {file = "mypy-0.950-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b5b5bd0ffb11b4aba2bb6d31b8643902c48f990cc92fda4e21afac658044f0c0"}, + {file = "mypy-0.950-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5e7647df0f8fc947388e6251d728189cfadb3b1e558407f93254e35abc026e22"}, + {file = "mypy-0.950-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eaff8156016487c1af5ffa5304c3e3fd183edcb412f3e9c72db349faf3f6e0eb"}, + {file = "mypy-0.950-cp310-cp310-win_amd64.whl", hash = "sha256:563514c7dc504698fb66bb1cf897657a173a496406f1866afae73ab5b3cdb334"}, + {file = "mypy-0.950-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:dd4d670eee9610bf61c25c940e9ade2d0ed05eb44227275cce88701fee014b1f"}, + {file = "mypy-0.950-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ca75ecf2783395ca3016a5e455cb322ba26b6d33b4b413fcdedfc632e67941dc"}, + {file = "mypy-0.950-cp36-cp36m-win_amd64.whl", hash = "sha256:6003de687c13196e8a1243a5e4bcce617d79b88f83ee6625437e335d89dfebe2"}, + {file = "mypy-0.950-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4c653e4846f287051599ed8f4b3c044b80e540e88feec76b11044ddc5612ffed"}, + {file = "mypy-0.950-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e19736af56947addedce4674c0971e5dceef1b5ec7d667fe86bcd2b07f8f9075"}, + {file = "mypy-0.950-cp37-cp37m-win_amd64.whl", hash = "sha256:ef7beb2a3582eb7a9f37beaf38a28acfd801988cde688760aea9e6cc4832b10b"}, + {file = "mypy-0.950-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0112752a6ff07230f9ec2f71b0d3d4e088a910fdce454fdb6553e83ed0eced7d"}, + {file = "mypy-0.950-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ee0a36edd332ed2c5208565ae6e3a7afc0eabb53f5327e281f2ef03a6bc7687a"}, + {file = "mypy-0.950-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:77423570c04aca807508a492037abbd72b12a1fb25a385847d191cd50b2c9605"}, + {file = "mypy-0.950-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5ce6a09042b6da16d773d2110e44f169683d8cc8687e79ec6d1181a72cb028d2"}, + {file = "mypy-0.950-cp38-cp38-win_amd64.whl", hash = "sha256:5b231afd6a6e951381b9ef09a1223b1feabe13625388db48a8690f8daa9b71ff"}, + {file = "mypy-0.950-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0384d9f3af49837baa92f559d3fa673e6d2652a16550a9ee07fc08c736f5e6f8"}, + {file = "mypy-0.950-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1fdeb0a0f64f2a874a4c1f5271f06e40e1e9779bf55f9567f149466fc7a55038"}, + {file = "mypy-0.950-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:61504b9a5ae166ba5ecfed9e93357fd51aa693d3d434b582a925338a2ff57fd2"}, + {file = "mypy-0.950-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a952b8bc0ae278fc6316e6384f67bb9a396eb30aced6ad034d3a76120ebcc519"}, + {file = "mypy-0.950-cp39-cp39-win_amd64.whl", hash = "sha256:eaea21d150fb26d7b4856766e7addcf929119dd19fc832b22e71d942835201ef"}, + {file = "mypy-0.950-py3-none-any.whl", hash = "sha256:a4d9898f46446bfb6405383b57b96737dcfd0a7f25b748e78ef3e8c576bba3cb"}, + {file = "mypy-0.950.tar.gz", hash = "sha256:1b333cfbca1762ff15808a0ef4f71b5d3eed8528b23ea1c3fb50543c867d68de"}, +] +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, +] +nox = [ + {file = "nox-2022.1.7-py3-none-any.whl", hash = "sha256:efee12f02d39405b16d68f60e7a06fe1fc450ae58669d6cdda8c7f48e3bae9e3"}, + {file = "nox-2022.1.7.tar.gz", hash = "sha256:b375238cebb0b9df2fab74b8d0ce1a50cd80df60ca2e13f38f539454fcd97d7e"}, +] +nox-poetry = [ + {file = "nox-poetry-0.9.0.tar.gz", hash = "sha256:ea48fa535cd048854da35af7c6c3e92046fbed9b9023bb81193fb4d2d3a47c92"}, + {file = "nox_poetry-0.9.0-py3-none-any.whl", hash = "sha256:33423c855fb47e2901faf9e15937326bc20c6e356eef825903eed4f8bbda69d3"}, +] +packaging = [ + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, +] +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, +] +pep8-naming = [ + {file = "pep8-naming-0.12.1.tar.gz", hash = "sha256:bb2455947757d162aa4cad55dba4ce029005cd1692f2899a21d51d8630ca7841"}, + {file = "pep8_naming-0.12.1-py2.py3-none-any.whl", hash = "sha256:4a8daeaeb33cfcde779309fc0c9c0a68a3bbe2ad8a8308b763c5068f86eb9f37"}, +] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] +pluggy = [ + {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, + {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, +] +py = [ + {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, + {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, +] +pycodestyle = [ + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, +] +pyflakes = [ + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, +] +pygments = [ + {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, + {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, +] +pymdown-extensions = [ + {file = "pymdown_extensions-9.4-py3-none-any.whl", hash = "sha256:5b7432456bf555ce2b0ab3c2439401084cda8110f24f6b3ecef952b8313dfa1b"}, + {file = "pymdown_extensions-9.4.tar.gz", hash = "sha256:1baa22a60550f731630474cad28feb0405c8101f1a7ddc3ec0ed86ee510bcc43"}, +] +pyparsing = [ + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, +] +pyproject-flake8 = [ + {file = "pyproject-flake8-0.0.1a4.tar.gz", hash = "sha256:8ed9453f1d984cfe94c998f9840275359e29e7f435b8ddd188ae084e2dc1270c"}, + {file = "pyproject_flake8-0.0.1a4-py2.py3-none-any.whl", hash = "sha256:1a8f94e18d08677ee780625049d9d00a9ee823661c6606caab8a383351037a75"}, +] +pytest = [ + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, +] +pytest-cov = [ + {file = "pytest-cov-3.0.0.tar.gz", hash = "sha256:e7f0f5b1617d2210a2cabc266dfe2f4c75a8d32fb89eafb7ad9d06f6d076d470"}, + {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, +] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] +pytkdocs = [ + {file = "pytkdocs-0.16.1-py3-none-any.whl", hash = "sha256:a8c3f46ecef0b92864cc598e9101e9c4cf832ebbf228f50c84aa5dd850aac379"}, + {file = "pytkdocs-0.16.1.tar.gz", hash = "sha256:e2ccf6dfe9dbbceb09818673f040f1a7c32ed0bffb2d709b06be6453c4026045"}, +] +pyyaml = [ + {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, + {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b"}, + {file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"}, + {file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"}, + {file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"}, + {file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"}, + {file = "PyYAML-6.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4"}, + {file = "PyYAML-6.0-cp36-cp36m-win32.whl", hash = "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293"}, + {file = "PyYAML-6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57"}, + {file = "PyYAML-6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4"}, + {file = "PyYAML-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9"}, + {file = "PyYAML-6.0-cp37-cp37m-win32.whl", hash = "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737"}, + {file = "PyYAML-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d"}, + {file = "PyYAML-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34"}, + {file = "PyYAML-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287"}, + {file = "PyYAML-6.0-cp38-cp38-win32.whl", hash = "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78"}, + {file = "PyYAML-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b"}, + {file = "PyYAML-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3"}, + {file = "PyYAML-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0"}, + {file = "PyYAML-6.0-cp39-cp39-win32.whl", hash = "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb"}, + {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, + {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, +] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] +requests = [ + {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, + {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, +] +six = [ + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, +] +soupsieve = [ + {file = "soupsieve-2.3.2.post1-py3-none-any.whl", hash = "sha256:3b2503d3c7084a42b1ebd08116e5f81aadfaea95863628c80a3b774a11b7c759"}, + {file = "soupsieve-2.3.2.post1.tar.gz", hash = "sha256:fc53893b3da2c33de295667a0e19f078c14bf86544af307354de5fcf12a3f30d"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, +] +tomlkit = [ + {file = "tomlkit-0.7.2-py2.py3-none-any.whl", hash = "sha256:173ad840fa5d2aac140528ca1933c29791b79a374a0861a80347f42ec9328117"}, + {file = "tomlkit-0.7.2.tar.gz", hash = "sha256:d7a454f319a7e9bd2e249f239168729327e4dd2d27b17dc68be264ad1ce36754"}, +] +typer = [ + {file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"}, + {file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"}, +] +typing-extensions = [ + {file = "typing_extensions-4.2.0-py3-none-any.whl", hash = "sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708"}, + {file = "typing_extensions-4.2.0.tar.gz", hash = "sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376"}, +] +urllib3 = [ + {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, + {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, +] +virtualenv = [ + {file = "virtualenv-20.14.1-py2.py3-none-any.whl", hash = "sha256:e617f16e25b42eb4f6e74096b9c9e37713cf10bf30168fb4a739f3fa8f898a3a"}, + {file = "virtualenv-20.14.1.tar.gz", hash = "sha256:ef589a79795589aada0c1c5b319486797c03b67ac3984c48c669c0e4f50df3a5"}, +] +watchdog = [ + {file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:177bae28ca723bc00846466016d34f8c1d6a621383b6caca86745918d55c7383"}, + {file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d1cf7dfd747dec519486a98ef16097e6c480934ef115b16f18adb341df747a4"}, + {file = "watchdog-2.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f14ce6adea2af1bba495acdde0e510aecaeb13b33f7bd2f6324e551b26688ca"}, + {file = "watchdog-2.1.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4d0e98ac2e8dd803a56f4e10438b33a2d40390a72750cff4939b4b274e7906fa"}, + {file = "watchdog-2.1.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:81982c7884aac75017a6ecc72f1a4fedbae04181a8665a34afce9539fc1b3fab"}, + {file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0b4a1fe6201c6e5a1926f5767b8664b45f0fcb429b62564a41f490ff1ce1dc7a"}, + {file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6e6ae29b72977f2e1ee3d0b760d7ee47896cb53e831cbeede3e64485e5633cc8"}, + {file = "watchdog-2.1.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9777664848160449e5b4260e0b7bc1ae0f6f4992a8b285db4ec1ef119ffa0e2"}, + {file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:19b36d436578eb437e029c6b838e732ed08054956366f6dd11875434a62d2b99"}, + {file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b61acffaf5cd5d664af555c0850f9747cc5f2baf71e54bbac164c58398d6ca7b"}, + {file = "watchdog-2.1.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e877c70245424b06c41ac258023ea4bd0c8e4ff15d7c1368f17cd0ae6e351dd"}, + {file = "watchdog-2.1.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d802d65262a560278cf1a65ef7cae4e2bc7ecfe19e5451349e4c67e23c9dc420"}, + {file = "watchdog-2.1.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3750ee5399e6e9c69eae8b125092b871ee9e2fcbd657a92747aea28f9056a5c"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ed6d9aad09a2a948572224663ab00f8975fae242aa540509737bb4507133fa2d"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_armv7l.whl", hash = "sha256:b26e13e8008dcaea6a909e91d39b629a39635d1a8a7239dd35327c74f4388601"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_i686.whl", hash = "sha256:0908bb50f6f7de54d5d31ec3da1654cb7287c6b87bce371954561e6de379d690"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64.whl", hash = "sha256:bdcbf75580bf4b960fb659bbccd00123d83119619195f42d721e002c1621602f"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:81a5861d0158a7e55fe149335fb2bbfa6f48cbcbd149b52dbe2cd9a544034bbd"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_s390x.whl", hash = "sha256:03b43d583df0f18782a0431b6e9e9965c5b3f7cf8ec36a00b930def67942c385"}, + {file = "watchdog-2.1.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ae934e34c11aa8296c18f70bf66ed60e9870fcdb4cc19129a04ca83ab23e7055"}, + {file = "watchdog-2.1.7-py3-none-win32.whl", hash = "sha256:49639865e3db4be032a96695c98ac09eed39bbb43fe876bb217da8f8101689a6"}, + {file = "watchdog-2.1.7-py3-none-win_amd64.whl", hash = "sha256:340b875aecf4b0e6672076a6f05cfce6686935559bb6d34cebedee04126a9566"}, + {file = "watchdog-2.1.7-py3-none-win_ia64.whl", hash = "sha256:351e09b6d9374d5bcb947e6ac47a608ec25b9d70583e9db00b2fcdb97b00b572"}, + {file = "watchdog-2.1.7.tar.gz", hash = "sha256:3fd47815353be9c44eebc94cc28fe26b2b0c5bd889dafc4a5a7cbdf924143480"}, +] +zipp = [ + {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, + {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, +] diff --git a/pyproject.toml b/pyproject.toml index da365d1..1208ac4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,73 @@ +[tool.poetry] +name = "fact" +version = "1.0.0" +description = "Example Python project using best practices" +authors = ["John Hagen "] +license = "MIT" +readme = "README.md" +repository = "https://github.com/johnthagen/python-blueprint" +documentation = "https://johnthagen.github.io/python-blueprint/" +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Typing :: Typed", +] + +[tool.poetry.dependencies] +python = "^3.8" +colorama = "*" +typer = "*" + +[tool.poetry.dev-dependencies] +nox-poetry = "*" + +# Testing. +pytest = "*" +pytest-cov = "*" + +# Type Checking. +mypy = "*" +# As of mypy 0.900, mypy no longer bundles the stubs for third-party libraries that reside +# in the typeshed project. Add these "types-" packages here if you depend on them in +# requirements.in (e.g. types-requests). +# See: http://mypy-lang.blogspot.com/2021/06/mypy-0900-released.html +# https://github.com/python/typeshed/tree/master/stubs + +# Linting. +flake8 = "*" +flake8-bugbear = "*" +flake8-broken-line = "*" +flake8-comprehensions = "*" +pep8-naming = "*" +# TODO: Remove this when flake8 adds native support for pyproject.toml. +pyproject-flake8 = "*" + +# Formatting. +black = "*" +isort = "*" + +# Documentation. +mkdocs-material = "*" +mkdocs-htmlproofer-plugin = "*" +mkdocstrings = { version = "*", extras = ["python"] } +## Autodoc. +mkdocs-gen-files = "*" +mkdocs-literate-nav = "*" + +[tool.poetry.scripts] +fact = "fact.cli:entry_point" + [build-system] -requires = ["setuptools", "wheel"] -build-backend = "setuptools.build_meta" +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" [tool.mypy] ignore_missing_imports = true @@ -8,8 +75,20 @@ strict = true # If certain strict config options are too pedantic for a project, # disable them selectively here by setting to false. -[tool.black] +# Note: This configuration is suppported by pyproject-flake8. +[tool.flake8] # Use the more relaxed max line length permitted in PEP 8. +max-line-length = 99 +# This ignore is required by black. +extend-ignore = "E203," +# flake8-bugbear config. +# This argument is not needed if not using Typer as a dependency. +extend-immutable-calls = "Argument," +# TODO: Remove this once flake8 > 4.0.1 is released as this has been fixed +# upstream: https://github.com/PyCQA/flake8/pull/1443 +extend-exclude = ".nox," + +[tool.black] line-length = 99 target-version = ["py38", "py39", "py310"] # black will automatically exclude all files listed in .gitignore diff --git a/requirements.in b/requirements.in deleted file mode 100644 index e2e7a41..0000000 --- a/requirements.in +++ /dev/null @@ -1,2 +0,0 @@ -colorama -typer diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 10fe6bf..0000000 --- a/requirements.txt +++ /dev/null @@ -1,14 +0,0 @@ -# -# This file is autogenerated by pip-compile with python 3.9 -# To update, run: -# -# pip-compile '.\requirements.in' -# -click==8.0.4 - # via typer -colorama==0.4.4 - # via - # -r .\requirements.in - # click -typer==0.4.0 - # via -r .\requirements.in diff --git a/setup.py b/setup.py deleted file mode 100755 index 4244600..0000000 --- a/setup.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python3 - -from pathlib import Path - -import setuptools - -project_dir = Path(__file__).parent - -setuptools.setup( - name="fact", - version="1.0.0", - description="Example Python project", - # Use UTF-8 encoding for README even on Windows by using the encoding argument. - long_description=project_dir.joinpath("README.md").read_text(encoding="utf-8"), - long_description_content_type="text/markdown", - keywords=["python"], - author="", - url="https://github.com/johnthagen/python-blueprint", - packages=setuptools.find_packages("src"), - package_dir={"": "src"}, - # pip 9.0+ will inspect this field when installing to help users install a - # compatible version of the library for their Python version. - python_requires=">=3.8", - # There are some peculiarities on how to include package data for source - # distributions using setuptools. You also need to add entries for package - # data to MANIFEST.in. - # See https://stackoverflow.com/questions/7522250/ - include_package_data=True, - # This file is required to inform mypy that inline type hints are used. - # See: https://mypy.readthedocs.io/en/stable/installed_packages.html - package_data={"fact": ["py.typed"]}, - # This is a trick to avoid duplicating dependencies between both setup.py and - # requirements.txt. - # requirements.txt must be included in MANIFEST.in for this to work. - # It does not work for all types of dependencies (e.g. VCS dependencies). - # For VCS dependencies, use pip >= 19 and the PEP 508 syntax. - # Example: requests @ git+https://github.com/requests/requests.git@branch_or_tag - # See: https://github.com/pypa/pip/issues/6162 - install_requires=project_dir.joinpath("requirements.txt").read_text().split("\n"), - zip_safe=False, - license="MIT", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Intended Audience :: Developers", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Typing :: Typed", - ], - entry_points={"console_scripts": ["fact=fact.cli:main"]}, -) diff --git a/src/fact/cli.py b/src/fact/cli.py index 33581d8..9596a13 100755 --- a/src/fact/cli.py +++ b/src/fact/cli.py @@ -17,6 +17,10 @@ def main(n: int = Argument(..., min=0, help="The input n of fact(n)")) -> None: ) +def entry_point() -> None: + typer.run(main) + + # Allow the script to be run standalone (useful during development). if __name__ == "__main__": - typer.run(main) + entry_point() diff --git a/tox.ini b/tox.ini index 4cb4270..25add0f 100644 --- a/tox.ini +++ b/tox.ini @@ -1,14 +1,3 @@ -[flake8] -# Use the more relaxed max line length permitted in PEP 8. -max-line-length = 99 -# This ignore is required by black. -extend-ignore = E203 -extend-exclude = - venv -# flake8-bugbear config. -# This argument is not needed if not using Typer as a dependency. -extend-immutable-calls = - Argument # This is the configuration for the tox-gh-actions plugin for GitHub Actions # https://github.com/ymyzk/tox-gh-actions From 69101162d57b88d34ea17e7bd2341053278fcf3d Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 15:49:16 -0400 Subject: [PATCH 02/20] Remove tox.ini --- .github/workflows/python.yml | 2 +- tox.ini | 111 ----------------------------------- 2 files changed, 1 insertion(+), 112 deletions(-) delete mode 100644 tox.ini diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 83b437e..e450218 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -3,7 +3,7 @@ name: python on: [push, pull_request] jobs: - tox: + nox: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 25add0f..0000000 --- a/tox.ini +++ /dev/null @@ -1,111 +0,0 @@ - -# This is the configuration for the tox-gh-actions plugin for GitHub Actions -# https://github.com/ymyzk/tox-gh-actions -# This section is not needed if not using GitHub Actions for CI. -[gh-actions] -python = - 3.8: py38 - 3.9: py39, fmt-check, lint, type-check, docs - 3.10: py310 - -[tox] -# These are the default environments that will be run -# when `tox` is run without arguments. -envlist = - fmt-check - lint - type-check - py{38,39,310} - docs -skip_missing_interpreters = true - -# Activate isolated build environment. tox will use a virtual environment -# to build a source distribution from the source tree. For build tools and -# arguments use the pyproject.toml file as specified in PEP-517 and PEP-518. -isolated_build = true - -[testenv] -deps = - -r{toxinidir}/requirements.txt - -r{toxinidir}/dev-requirements.txt -commands = - {envpython} -m pytest --cov=fact --cov-report=html --cov-report=term {posargs} - -[testenv:type-check] -skip_install = true -deps = - # It is important to install the main project requirements, as some - # packages many contain inline type hints (PEP 561) that mypy will use. - -r{toxinidir}/requirements.txt - -r{toxinidir}/dev-requirements.txt -commands = - mypy src tests - -[testenv:lint] -skip_install = true -deps = - -r{toxinidir}/dev-requirements.txt -commands = - flake8 - -[testenv:fmt] -skip_install = true -deps = - -r{toxinidir}/dev-requirements.txt -commands = - isort . - black . - -[testenv:fmt-check] -skip_install = true -deps = - -r{toxinidir}/dev-requirements.txt -commands = - isort --check . - black --check . - -[testenv:docs] -skip_install = true -# Needed for mkdocstrings-python to locate source files. -setenv = - PYTHONPATH = src -commands = - mkdocs build - -[testenv:docs-serve] -skip_install = true -setenv = - PYTHONPATH = src -commands = - mkdocs serve - -[testenv:docs-github-pages] -skip_install = true -setenv = - PYTHONPATH = src -commands = - # See: https://squidfunk.github.io/mkdocs-material/publishing-your-site/ - mkdocs gh-deploy --force - -[testenv:build] -skip_install = true -deps = - build -commands = - {envpython} -m build - -[testenv:upload] -skip_install = true -deps = - twine -commands = - {envpython} -m twine upload {toxinidir}/dist/* - -[testenv:licenses] -skip_install = true -recreate = true -deps = - -r{toxinidir}/requirements.txt - pip-licenses -commands = - pip-licenses {posargs} From da65d444c3d55e297efc8c6e34dea60eb5b0ead0 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 15:51:53 -0400 Subject: [PATCH 03/20] Reorganize .gitignore --- .gitignore | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index 349ff56..550fbfa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,13 @@ .idea -*.egg-info .mypy_cache .nox .pytest_cache -__pycache__ -.coverage -.coverage.* -htmlcov -*.pyc dist build +htmlcov site +__pycache__ +*.pyc +*.egg-info +.coverage +.coverage.* From 7819bab3d43b980703932be1b226af0728317d2c Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 16:40:15 -0400 Subject: [PATCH 04/20] Manually run CI --- .github/workflows/python.yml | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e450218..e8f19aa 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -5,12 +5,20 @@ on: [push, pull_request] jobs: nox: runs-on: ubuntu-latest + strategy: + matrix: + python-version: [ "3.8", "3.9", "3.10" ] steps: - - uses: actions/checkout@v2 - - uses: excitedleigh/setup-nox@v2.0.0 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install nox-poetry + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Upgrade pip + run: python -m pip install --upgrade pip + - name: Install Poetry + run: pipx install poetry + - name: Install nox-poetry + run: pipx install nox-poetry - name: Test with Nox - run: nox + run: pipx run nox From 49efa8a437d5a6633fdca6739045de4ce0363f7d Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 16:42:31 -0400 Subject: [PATCH 05/20] Fix pipx --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index e8f19aa..d64509f 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -19,6 +19,6 @@ jobs: - name: Install Poetry run: pipx install poetry - name: Install nox-poetry - run: pipx install nox-poetry + run: pipx install nox-poetry --include-deps - name: Test with Nox run: pipx run nox From c9d5b64011b34ebf2bd38c67df342d121721cef2 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 16:44:37 -0400 Subject: [PATCH 06/20] Remove pipx from nox invocation --- .github/workflows/python.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index d64509f..5c765d9 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -21,4 +21,4 @@ jobs: - name: Install nox-poetry run: pipx install nox-poetry --include-deps - name: Test with Nox - run: pipx run nox + run: nox From e4a08dff61f90202910b2adf100f9ee89acfa974 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 16:49:37 -0400 Subject: [PATCH 07/20] Fix Pages CI --- .github/workflows/pages.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index fef9360..0855b6c 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -11,12 +11,15 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-python@v3 with: python-version: "3.9" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install tox - - run: tox -e docs-github-pages + - name: Upgrade pip + run: python -m pip install --upgrade pip + - name: Install Poetry + run: pipx install poetry + - name: Install nox-poetry + run: pipx install nox-poetry --include-deps + - name: Publish Docs + run: nox -s docs_github_pages From 828fb9c8b24844852bb306f43a73f0e34dd01e95 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 20:13:07 -0400 Subject: [PATCH 08/20] Update README --- README.md | 167 ++++++++++++++++++++--------------------------------- noxfile.py | 1 + 2 files changed, 65 insertions(+), 103 deletions(-) diff --git a/README.md b/README.md index cab1187..f535f58 100644 --- a/README.md +++ b/README.md @@ -17,74 +17,40 @@ and a command line interface (`fact.cli`). Python 3.8+. -# Dependencies +# Package Management -Dependencies are defined in: +This package uses [Poetry](https://python-poetry.org/) to manage dependencies and +isolated [Python virtual environments](https://docs.python.org/3/library/venv.html). -- [`requirements.in`](./requirements.in) -- [`requirements.txt`](./requirements.txt) -- [`dev-requirements.in`](./dev-requirements.in) -- [`dev-requirements.txt`](./dev-requirements.txt) +To proceed, +[install Poetry globally onto your system](https://python-poetry.org/docs/#installation). -## Virtual Environments +## Dependencies -It is best practice during development to create an -isolated [Python virtual environment](https://docs.python.org/3/library/venv.html) using the `venv` -standard library module. This will keep dependant Python packages from interfering with other -Python projects on your system. +Dependencies are defined in [`pyproject.toml`](./pyproject.toml) and specific versions are locked +into [`poetry.lock`](./poetry.lock). This allows for exact reproducible environments across +all machines that use the project, both during development and in production. -On *Nix: +To install all dependencies into an isolated virtual environment: ```bash -# On Python 3.9+, add --upgrade-deps -$ python3 -m venv venv -$ source venv/bin/activate +# Append --remove-untracked to remove any dependencies no longer in use. +$ poetry install ``` -On Windows Powershell using the [`py` launcher](https://www.python.org/dev/peps/pep-0397/): +To activate the virtual environment that is automatically created by Poetry: ```bash -> py -m venv venv -> venv\Scripts\activate +$ poetry shell ``` -Once activated, it is a good practice to update core packaging tools (`pip`, `setuptools`, -and `wheel`) to the latest versions. +To upgrade all dependencies to their latest versions: ```bash -(venv) $ python -m pip install --upgrade pip setuptools wheel +$ poetry update ``` -## (Applications Only) Locking Dependencies - -This project uses [pip-tools](https://github.com/jazzband/pip-tools) to lock project dependencies -and create reproducible virtual environments. - -**Note:** *Library* projects should not lock their `requirements.txt`. Since `python-blueprint` -also has a CLI application, this end-user application example is used to demonstrate how to lock -application dependencies. - -To update dependencies: - -```bash -(venv) $ python -m pip install pip-tools -(venv) $ python -m piptools compile --upgrade requirements.in -(venv) $ python -m piptools compile --upgrade dev-requirements.in -``` - -After upgrading dependencies, run the unit tests as described in the [Unit Testing](#unit-testing) -section to ensure that none of the updated packages caused incompatibilities in the current -project. - -## Syncing Virtual Environments - -To cleanly install your dependencies into your virtual environment: - -```bash -(venv) $ python -m piptools sync requirements.txt dev-requirements.txt -``` - -# Packaging +## Packaging This project is designed as a Python package, meaning that it can be bundled up and redistributed as a single compressed file. @@ -92,16 +58,13 @@ as a single compressed file. Packaging is configured by: - [`pyproject.toml`](./pyproject.toml) -- [`setup.py`](./setup.py) -- [`MANIFEST.in`](./MANIFEST.in) To package the project as both a [source distribution](https://docs.python.org/3/distutils/sourcedist.html) and -a [wheel](https://wheel.readthedocs.io/en/stable/) using the -[`build`](https://pypa-build.readthedocs.io/en/stable/) package: +a [wheel](https://wheel.readthedocs.io/en/stable/): ```bash -(venv) $ tox -e build +$ poetry build ``` This will generate `dist/fact-1.0.0.tar.gz` and `dist/fact-1.0.0-py3-none-any.whl`. @@ -109,32 +72,30 @@ This will generate `dist/fact-1.0.0.tar.gz` and `dist/fact-1.0.0-py3-none-any.wh Read more about the [advantages of wheels](https://pythonwheels.com/) to understand why generating wheel distributions are important. -## Upload Distributions to PyPI +## Publish Distributions to PyPI Source and wheel redistributable packages can -be [uploaded to PyPI](https://packaging.python.org/tutorials/packaging-projects/) or installed +be [published to PyPI](https://python-poetry.org/docs/cli#publish) or installed directly from the filesystem using `pip`. -After running the `build` `tox` environment, to upload to PyPI using -[`twine`](https://twine.readthedocs.io/en/latest/): - ```bash -(venv) $ tox -e upload +$ poetry publish ``` # Enforcing Code Quality Automated code quality checks are performed using -[tox](https://tox.readthedocs.io/en/latest/index.html). tox will automatically create virtual -environments based on [`tox.ini`](./tox.ini) for unit testing, PEP 8 style guide checking, -type checking and documentation generation. +[Nox](https://nox.thea.codes/en/stable/) and +[`nox-poetry`](https://nox-poetry.readthedocs.io/en/stable/). Nox will automatically create virtual +environments and run commands based on [`noxfile.py`](./noxfile.py) for unit testing, PEP 8 style +guide checking, type checking and documentation generation. ```bash -# Run all environments. -# To only run a single environment, specify it like: -e lint -# Note: tox is installed into the virtual environment automatically by `piptools sync` -# command above. -(venv) $ tox +# Run all sessions. +# To only run a single session, append its name: -s lint +# Note: Nox is installed into the virtual environment automatically by `poetry install` +# command above. Run `poetry shell` to activate the virtual environment. +(fact) $ nox ``` ## Unit Testing @@ -156,29 +117,31 @@ be named the same, even if they are in different subdirectories. Code coverage is provided by the [pytest-cov](https://pytest-cov.readthedocs.io/en/latest/) plugin. -When running a unit test tox environment (e.g. `tox -e py`), an HTML report is generated in +When running a unit test Nox session (e.g. `nox -s test`), an HTML report is generated in the `htmlcov` folder showing each source file and which lines were executed during unit testing. Open `htmlcov/index.html` in a web browser to view the report. Code coverage reports help identify areas of the project that are currently not tested. pytest and code coverage are configured in [`pyproject.toml`](./pyproject.toml). -To pass arguments to `pytest` through `tox`: +To pass arguments to `pytest` through `nox`: ```bash -(venv) $ tox -e py -- -k invalid_factorial +(fact) $ nox -s test -- -k invalid_factorial ``` ## Code Style Checking [PEP 8](https://peps.python.org/pep-0008/) is the universally accepted style guide for Python code. PEP 8 code compliance is verified using [Flake8](http://flake8.pycqa.org/). Flake8 is -configured in the `[flake8]` section of `tox.ini`. Extra Flake8 plugins are also included: +configured in the `[tool.flake8]` section of `pyproject.toml`. Extra Flake8 plugins are also +included: - `flake8-bugbear`: Find likely bugs and design problems in your program. - `flake8-broken-line`: Forbid using backslashes (`\`) for line breaks. - `flake8-comprehensions`: Helps write better `list`/`set`/`dict` comprehensions. - `pep8-naming`: Ensure functions, classes, and variables are named with correct casing. +- `pyproject-flake8`: Allow configuration of `flake8` through `pyproject.toml`. Some code style settings are included in [`.editorconfig`](./.editorconfig) and will be configured automatically in editors such as PyCharm. @@ -195,13 +158,13 @@ These tools are configured by: To automatically format code, run: ```bash -(venv) $ tox -e fmt +(fact) $ nox -s fmt ``` To verify code has been formatted, such as in a CI job: ```bash -(venv) $ tox -e fmt-check +(fact) $ nox -s fmt_check ``` ## Type Checking @@ -221,7 +184,7 @@ def factorial(n: int) -> int: ... ``` -Type checking is performed by mypy via `tox -e type-check`. mypy is configured in +Type checking is performed by mypy via `nox -s type_check`. mypy is configured in [`pyproject.toml`](./pyproject.toml). See also [awesome-python-typing](https://github.com/typeddjango/awesome-python-typing). @@ -241,9 +204,7 @@ installed package to indicate that inline type annotations should be checked. Continuous integration is provided by [GitHub Actions](https://github.com/features/actions). This runs all tests, lints, and type checking for every commit and pull request to the repository. -GitHub Actions is configured in [`.github/workflows/python.yml`](./.github/workflows/python.yml) -and [`tox.ini`](./tox.ini) using the -[tox-gh-actions plugin](https://github.com/ymyzk/tox-gh-actions). +GitHub Actions is configured in [`.github/workflows/python.yml`](./.github/workflows/python.yml). # Documentation @@ -256,16 +217,16 @@ the power of Markdown. This makes it a great fit for user guides and other techn The example MkDocs project included in this project is configured to allow the built documentation to be hosted at any URL or viewed offline from the file system. -To build the user guide, run `tox -e docs`. Open `docs/user_guide/site/index.html` using +To build the user guide, run `nox -s docs`. Open `docs/user_guide/site/index.html` using a web browser. To build and serve the user guide with automatic rebuilding as you change the contents, -run `tox -e docs-serve` and open in a browser. +run `nox -s docs_serve` and open in a browser. Each time the `master` Git branch is updated, the [`.github/workflows/pages.yml`](.github/workflows/pages.yml) GitHub Action will automatically build the user guide and publish it to [GitHub Pages](https://pages.github.com/). -This is configured in the `docs-user-guide-github-pages` `tox` environment. This hosted user guide +This is configured in the `docs_github_pages` Nox session. This hosted user guide can be viewed at . ## Generating API Documentation @@ -304,25 +265,25 @@ fact ├── tests │ ├── __init__.py │ └── test_fact.py -├── tox.ini -└── setup.py +├── noxfile.py +└── pyproject.toml ``` However, this structure is [known](https://docs.pytest.org/en/latest/goodpractices.html#tests-outside-application-code) to -have bad interactions with `pytest` and `tox`, two standard tools maintaining Python projects. The -fundamental issue is that tox creates an isolated virtual environment for testing. By installing -the distribution into the virtual environment, `tox` ensures that the tests pass even after the +have bad interactions with `pytest` and `nox`, two standard tools maintaining Python projects. The +fundamental issue is that Nox creates an isolated virtual environment for testing. By installing +the distribution into the virtual environment, `nox` ensures that the tests pass even after the distribution has been packaged and installed, thereby catching any errors in packaging and installation scripts, which are common. Having the Python packages in the project root subverts this isolation for two reasons: 1. Calling `python` in the project root (for example, `python -m pytest tests/`) - [causes Python to add the current working directory](https://docs.pytest.org/en/latest/pythonpath.html#invoking-pytest-versus-python-m-pytest) ( - the project root) to `sys.path`, which Python uses to find modules. Because the source - package `fact` is in the project root, it shadows the `fact` package installed in the tox - environment. -2. Calling `pytest` directly anywhere that it can find the tests will also add the project root + [causes Python to add the current working directory](https://docs.pytest.org/en/latest/pythonpath.html#invoking-pytest-versus-python-m-pytest) + (the project root) to `sys.path`, which Python uses to find modules. Because the source + package `fact` is in the project root, it shadows the `fact` package installed in the Nox + session. +3. Calling `pytest` directly anywhere that it can find the tests will also add the project root to `sys.path` if the `tests` folder is a Python package (that is, it contains a `__init__.py` file). [pytest adds all folders containing packages](https://docs.pytest.org/en/latest/goodpractices.html#conventions-for-python-test-discovery) @@ -331,14 +292,14 @@ this isolation for two reasons: In order to properly test the project, the source packages must not be on the Python path. To prevent this, there are three possible solutions: -1. Remove the `__init__.py` file from `tests` and run `pytest` directly as a tox command. +1. Remove the `__init__.py` file from `tests` and run `pytest` directly as a Nox session. 2. Remove the `__init__.py` file from tests and change the working directory of `python -m pytest` to `tests`. 3. Move the source packages to a dedicated `src` folder. The dedicated `src` directory is the [recommended solution](https://docs.pytest.org/en/latest/pythonpath.html#test-modules-conftest-py-files-inside-packages) -by `pytest` when using tox and the solution this blueprint promotes because it is the least brittle +by `pytest` when using Nox and the solution this blueprint promotes because it is the least brittle even though it deviates from the traditional Python project structure. It results is a directory structure like: @@ -352,8 +313,8 @@ fact ├── tests │ ├── __init__.py │ └── test_fact.py -├── tox.ini -└── setup.py +├── noxfile.py +└── pyproject.toml ``` # Licensing @@ -361,16 +322,16 @@ fact Licensing for the project is defined in: - [`LICENSE.txt`](./LICENSE.txt) -- [`setup.py`](./setup.py) +- [`pyproject.toml`](./pyproject.toml) This project uses a common permissive license, the MIT license. You may also want to list the licenses of all the packages that your Python project depends on. -To automatically list the licenses for all dependencies in `requirements.txt` (and their transitive -dependencies) using [pip-licenses](https://github.com/raimon49/pip-licenses): +To automatically list the licenses for all dependencies in (and their transitive dependencies) +using [pip-licenses](https://github.com/raimon49/pip-licenses): ```bash -(venv) $ tox -e licenses +(fact) $ nox -s licenses ... Name Version License colorama 0.4.3 BSD License @@ -445,6 +406,6 @@ To integrate automatic code formatters into PyCharm, reference the following ins > **Tip** > > These tools work best if you properly mark directories as excluded from the project that should -> be, such as `.tox`. See +> be, such as `.nox`. See > on -> how to Right Click | Mark Directory as | Excluded. +> how to Right-Click | Mark Directory as | Excluded. diff --git a/noxfile.py b/noxfile.py index 684103a..3a53759 100644 --- a/noxfile.py +++ b/noxfile.py @@ -42,6 +42,7 @@ def lint(s: Session) -> None: "pep8-naming", "pyproject-flake8", ) + # Run pyproject-flake8 entrypoint to support reading configuration from pyproject.toml. s.run("pflake8") From ab7d8f1ed715417c0b6bbb6edce422449fa9d514 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sat, 14 May 2022 20:16:30 -0400 Subject: [PATCH 09/20] Reorganize Dockerfile --- Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index a0f80eb..e2c9b53 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,6 +10,9 @@ # will need to be recompiled fully within the Docker images, increasing build times. FROM python:3.9-slim-bullseye AS python_builder +# Pin Poetry to a specific version to make Docker builds reproducible. +ENV POETRY_VERSION 1.1.13 + # Set ENV variables that make Python more friendly to running inside a container. ENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONBUFFERED 1 @@ -23,9 +26,6 @@ ENV WORKDIR /src # absoulte symlinks in it. ENV VIRTUAL_ENV /opt/venv -# Pin Poetry to a specific version to make Docker builds reproducible. -ENV POETRY_VERSION 1.1.13 - WORKDIR ${WORKDIR} # Install any system depdendencies required to build wheels, such as C compilers or system packages From 4c9529b66d9817e8eaf7f79f31210dd05e2169c5 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 06:33:25 -0400 Subject: [PATCH 10/20] Update README.md Co-authored-by: David Hagen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f535f58..ce46467 100644 --- a/README.md +++ b/README.md @@ -283,7 +283,7 @@ this isolation for two reasons: (the project root) to `sys.path`, which Python uses to find modules. Because the source package `fact` is in the project root, it shadows the `fact` package installed in the Nox session. -3. Calling `pytest` directly anywhere that it can find the tests will also add the project root +2. Calling `pytest` directly anywhere that it can find the tests will also add the project root to `sys.path` if the `tests` folder is a Python package (that is, it contains a `__init__.py` file). [pytest adds all folders containing packages](https://docs.pytest.org/en/latest/goodpractices.html#conventions-for-python-test-discovery) From b5e82d379b033b50432c3faef05c5353fcb10ae5 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 08:49:25 -0400 Subject: [PATCH 11/20] Reuse the existing poetry environment for simple project scripts --- .github/workflows/python.yml | 2 +- noxfile.py | 45 ++++++++---------------------------- 2 files changed, 11 insertions(+), 36 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 5c765d9..7048455 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -19,6 +19,6 @@ jobs: - name: Install Poetry run: pipx install poetry - name: Install nox-poetry - run: pipx install nox-poetry --include-deps + run: poetry install --no-root - name: Test with Nox run: nox diff --git a/noxfile.py b/noxfile.py index 3a53759..fb81035 100644 --- a/noxfile.py +++ b/noxfile.py @@ -4,12 +4,10 @@ nox.options.reuse_existing_virtualenvs = True nox.options.sessions = ["fmt_check", "type_check", "lint", "test", "docs"] -test_dependencies = ["pytest", "pytest-cov"] - @session(python=["3.8", "3.9", "3.10"]) def test(s: Session) -> None: - s.install(".", *test_dependencies) + s.install(".", "pytest", "pytest-cov") s.run( "python", "-m", @@ -24,72 +22,49 @@ def test(s: Session) -> None: ) -@session +# For some sessions, set venv_backend="none" to simply execute scripts using the existing +# environment. This requires that nox is installed and run using `poetry install`. +@session(venv_backend="none") def type_check(s: Session) -> None: # It is important to install the main project and test dependencies, as some packages contain # inline type hints (PEP 561) that mypy will use. - s.install(".", *test_dependencies, "mypy") s.run("mypy", "src", "tests") -@session +@session(venv_backend="none") def lint(s: Session) -> None: - s.install( - "flake8", - "flake8-bugbear", - "flake8-broken-line", - "flake8-comprehensions", - "pep8-naming", - "pyproject-flake8", - ) # Run pyproject-flake8 entrypoint to support reading configuration from pyproject.toml. s.run("pflake8") -fmt_dependencies = ["isort", "black"] - - -@session +@session(venv_backend="none") def fmt(s: Session) -> None: - s.install(*fmt_dependencies) s.run("isort", ".") s.run("black", ".") -@session +@session(venv_backend="none") def fmt_check(s: Session) -> None: - s.install(*fmt_dependencies) s.run("isort", "--check", ".") s.run("black", "--check", ".") -doc_dependencies = [ - "mkdocs-material", - "mkdocs-htmlproofer-plugin", - "mkdocstrings[python]", - "mkdocs-gen-files", - "mkdocs-literate-nav", -] - # Environment variable needed for mkdocstrings-python to locate source files. doc_env = {"PYTHONPATH": "src"} -@session +@session(venv_backend="none") def docs(s: Session) -> None: - s.install(*doc_dependencies) s.run("mkdocs", "build", env=doc_env) -@session +@session(venv_backend="none") def docs_serve(s: Session) -> None: - s.install(*doc_dependencies) s.run("mkdocs", "serve", env=doc_env) -@session +@session(venv_backend="none") def docs_github_pages(s: Session) -> None: - s.install(*doc_dependencies) s.run("mkdocs", "gh-deploy", "--force", env=doc_env) From dc8549a2cc438cf1b05bf5082de16c68ab15adfb Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 08:51:26 -0400 Subject: [PATCH 12/20] Activate Poetry environment in CI --- .github/workflows/python.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 7048455..1aa6b50 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -18,7 +18,9 @@ jobs: run: python -m pip install --upgrade pip - name: Install Poetry run: pipx install poetry - - name: Install nox-poetry - run: poetry install --no-root + - name: Install all dependencies, including Nox + run: | + poetry install --no-root + poetry shell - name: Test with Nox run: nox From d42c6d7fd68fa424581c93464cccabcba6966c27 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 08:57:45 -0400 Subject: [PATCH 13/20] Do not create Poetry venv --- .github/workflows/python.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 1aa6b50..fb55f72 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -19,8 +19,9 @@ jobs: - name: Install Poetry run: pipx install poetry - name: Install all dependencies, including Nox - run: | - poetry install --no-root - poetry shell + run: poetry install --no-root + env: + POETRY_VIRTUALENVS_CREATE: false + POETRY_VIRTUALENVS_CREATE: false - name: Test with Nox run: nox From 03c987646f8f8853a51246418d52c2053820ee4b Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 08:59:35 -0400 Subject: [PATCH 14/20] Fix GitHub Actions YAML --- .github/workflows/python.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index fb55f72..0965909 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -22,6 +22,5 @@ jobs: run: poetry install --no-root env: POETRY_VIRTUALENVS_CREATE: false - POETRY_VIRTUALENVS_CREATE: false - name: Test with Nox run: nox From 737f6ee4ce358351648fc628bc07902a41a63438 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 09:01:42 -0400 Subject: [PATCH 15/20] Use `poetry run nox` in CI --- .github/workflows/python.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 0965909..dd35d6b 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -20,7 +20,5 @@ jobs: run: pipx install poetry - name: Install all dependencies, including Nox run: poetry install --no-root - env: - POETRY_VIRTUALENVS_CREATE: false - name: Test with Nox - run: nox + run: poetry run nox From 456f2d130ecea8ab5df1dc6f4bab1c6a931025c9 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 09:27:04 -0400 Subject: [PATCH 16/20] Notate upstream Poetry features that would improve Docker build --- Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index e2c9b53..f4717ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,11 +36,14 @@ WORKDIR ${WORKDIR} # Install Poetry into the global environment to isolate it from the venv. This prevents Poetry # from uninstalling parts of itself. +# TODO: Improve Poetry usage in multi-stage Dockerfiles once these issues are fixed in Poetry. +# Non-editable `poetry install`: https://github.com/python-poetry/poetry/issues/1382 +# Specifying venv path: https://github.com/python-poetry/poetry/issues/1579 RUN pip install "poetry==${POETRY_VERSION}" # Copy in project dependency specification. COPY pyproject.toml poetry.lock ./ -RUN poetry export --format requirements.txt --output requirements.txt +RUN poetry export --output requirements.txt # Pre-download/compile wheel dependencies into a virtual environment. # Doing this in a multi-stage build allows ommitting compile dependencies from the final image. From 71283d1da71fff8b4a3b053c5b2ef893caa6bda4 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 13:42:16 -0400 Subject: [PATCH 17/20] Update Dockerfile Co-authored-by: David Hagen --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f4717ba..6448501 100644 --- a/Dockerfile +++ b/Dockerfile @@ -57,7 +57,7 @@ RUN pip install --upgrade pip wheel && \ COPY README.md ./ COPY src src -# Don't install the package itself with Poetry becuase it will install it as an editable install. +# Don't install the package itself with Poetry because it will install it as an editable install. RUN poetry build && \ pip install dist/*.whl From fe76e325985cb68703da9190056eeae670ade549 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 13:42:28 -0400 Subject: [PATCH 18/20] Update README.md Co-authored-by: David Hagen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ce46467..6f1e671 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ To package the project as both a a [wheel](https://wheel.readthedocs.io/en/stable/): ```bash -$ poetry build +$ poetry build ``` This will generate `dist/fact-1.0.0.tar.gz` and `dist/fact-1.0.0-py3-none-any.whl`. From 9725e0e02d05474e0f5f81b900556f980f354b10 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 13:45:41 -0400 Subject: [PATCH 19/20] Update GitHub Pages CI --- .github/workflows/pages.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 0855b6c..d81dd4a 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -19,7 +19,7 @@ jobs: run: python -m pip install --upgrade pip - name: Install Poetry run: pipx install poetry - - name: Install nox-poetry - run: pipx install nox-poetry --include-deps + - name: Install all dependencies, including Nox + run: poetry install --no-root - name: Publish Docs - run: nox -s docs_github_pages + run: poetry run nox -s docs_github_pages From ebf8bdfac67ed7549036c0a6339dff8f259b3dc4 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 15 May 2022 13:47:17 -0400 Subject: [PATCH 20/20] Clarify venv_backend="none" description --- noxfile.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/noxfile.py b/noxfile.py index fb81035..de23322 100644 --- a/noxfile.py +++ b/noxfile.py @@ -22,8 +22,8 @@ def test(s: Session) -> None: ) -# For some sessions, set venv_backend="none" to simply execute scripts using the existing -# environment. This requires that nox is installed and run using `poetry install`. +# For some sessions, set venv_backend="none" to simply execute scripts within the existing Poetry +# environment. This requires that nox is run within `poetry shell` or using `poetry run nox ...`. @session(venv_backend="none") def type_check(s: Session) -> None: # It is important to install the main project and test dependencies, as some packages contain