diff --git a/.flake8 b/.flake8 index 5d8971e9..2fbeb422 100644 --- a/.flake8 +++ b/.flake8 @@ -13,4 +13,4 @@ max-complexity = 10 import-order-style = pep8 application-import-names = pybotics max-line-length = 88 -ignore = E741,W503 +extend-ignore = E203,E741 diff --git a/Makefile b/Makefile index ce0076ac..f03361de 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ check-package: .PHONY: check-typing check-typing: - poetry run mypy --strict . + poetry run mypy --strict --show-error-codes . .PHONY: check-format check-format: @@ -22,6 +22,11 @@ lint: .PHONY: check check: check-format check-package check-typing lint +.PHONY: debug +debug: + @echo "Git version: $(shell git describe --tags)" + poetry debug + # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # formatting diff --git a/README.md b/README.md index 2903b3d1..d4877943 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ Social | ![Twitter Follow](https://img.shields.io/twitter/follow/engnadeau?style - [Citing](#citing) - [Development and Community Guidelines](#development-and-community-guidelines) - [Local Development](#local-development) + - [Docker Development](#docker-development) - [Dependency Management](#dependency-management) - [Submit an Issue](#submit-an-issue) - [Contributing](#contributing) @@ -135,6 +136,23 @@ Please cite the following articles if you use `pybotics` in your research: poetry install ``` +### Docker Development + +- Docker is a great tool to test the package in an isolated environment +- It is especially useful for debugging issues between python versions + +```bash +# launch container attached to current directory +docker run -v $(pwd):/$(basename $(pwd)) -w /$(basename $(pwd)) -it python:3 bash + +# install deps +pip install poetry +poetry install + +# run tests +make test +``` + ### Dependency Management ```bash diff --git a/examples/basic_usage.py b/examples/basic_usage.py index 6b238f47..ae020065 100644 --- a/examples/basic_usage.py +++ b/examples/basic_usage.py @@ -1,11 +1,13 @@ """Basic usage of the pybotics package.""" +import numpy as np + from pybotics.geometry import vector_2_matrix from pybotics.predefined_models import ur10 from pybotics.robot import Robot from pybotics.tool import Tool -def main(): +def main() -> None: """ Demonstrate pybotics usage. @@ -16,11 +18,11 @@ def main(): # add tool tool = Tool() - tool.position = [1, 2, 3] + tool.position = np.array([1, 2, 3]) robot.tool = tool # set world frame - world_frame = vector_2_matrix([100, 200, 300, 0, 0, 0]) + world_frame = vector_2_matrix(np.array([100, 200, 300, 0, 0, 0])) robot.world_frame = world_frame print(f"Robot: {robot}") diff --git a/poetry.lock b/poetry.lock index 337d0b2c..6e3952d5 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,14 +6,6 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "appnope" version = "0.1.0" @@ -90,23 +82,28 @@ python-versions = "*" [[package]] name = "black" -version = "19.10b0" +version = "22.6.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] -appdirs = "*" -attrs = ">=18.1.0" -click = ">=6.5" -pathspec = ">=0.6,<1" -regex = "*" -toml = ">=0.9.4" -typed-ast = ">=1.4.0" +click = ">=8.0.0" +ipython = {version = ">=7.8.0", optional = true, markers = "extra == \"jupyter\""} +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tokenize-rt = {version = ">=3.2.0", optional = true, markers = "extra == \"jupyter\""} +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +uvloop = ["uvloop (>=0.15.2)"] +jupyter = ["tokenize-rt (>=3.2.0)", "ipython (>=7.8.0)"] +d = ["aiohttp (>=3.7.4)"] +colorama = ["colorama (>=0.4.3)"] [[package]] name = "bleach" @@ -153,11 +150,15 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "7.1.2" +version = "8.1.3" description = "Composable command line interface toolkit" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} [[package]] name = "colorama" @@ -169,14 +170,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "5.2.1" +version = "6.4.4" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.7" [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "cycler" @@ -221,19 +222,30 @@ category = "dev" optional = false python-versions = ">=2.7" +[[package]] +name = "exceptiongroup" +version = "1.0.0rc8" +description = "Backport of PEP 654 (exception groups)" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "flake8" -version = "3.8.3" +version = "4.0.1" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.6.0a1,<2.7.0" -pyflakes = ">=2.2.0,<2.3.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "flake8-bugbear" @@ -247,29 +259,6 @@ python-versions = ">=3.6" attrs = ">=19.2.0" flake8 = ">=3.0.0" -[[package]] -name = "flake8-docstrings" -version = "1.5.0" -description = "Extension for flake8 which uses pydocstyle to check docstrings" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -flake8 = ">=3" -pydocstyle = ">=2.1" - -[[package]] -name = "flake8-import-order" -version = "0.18.1" -description = "Flake8 and pylama plugin that checks the ordering of import statements." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -pycodestyle = "*" - [[package]] name = "flake8-polyfill" version = "1.0.2" @@ -283,28 +272,32 @@ flake8 = "*" [[package]] name = "hypothesis" -version = "5.26.0" +version = "6.54.4" description = "A library for property-based testing" category = "dev" optional = false -python-versions = ">=3.5.2" +python-versions = ">=3.7" [package.dependencies] attrs = ">=19.2.0" +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} sortedcontainers = ">=2.1.0,<3.0.0" [package.extras] -all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "numpy (>=1.9.0)", "pandas (>=0.19)", "pytest (>=4.3)", "python-dateutil (>=1.4)", "pytz (>=2014.1)"] -cli = ["click (>=7.0)", "black (>=19.10b0)"] +all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=3.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=1.0)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.1)"] +cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"] +codemods = ["libcst (>=0.3.16)"] dateutil = ["python-dateutil (>=1.4)"] -django = ["pytz (>=2014.1)", "django (>=2.2)"] +django = ["django (>=3.2)"] dpcontracts = ["dpcontracts (>=0.4)"] ghostwriter = ["black (>=19.10b0)"] lark = ["lark-parser (>=0.6.5)"] numpy = ["numpy (>=1.9.0)"] -pandas = ["pandas (>=0.19)"] -pytest = ["pytest (>=4.3)"] +pandas = ["pandas (>=1.0)"] +pytest = ["pytest (>=4.6)"] pytz = ["pytz (>=2014.1)"] +redis = ["redis (>=3.0.0)"] +zoneinfo = ["backports.zoneinfo (>=0.2.1)", "tzdata (>=2022.1)"] [[package]] name = "idna" @@ -334,8 +327,8 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" zipp = ">=0.5" [package.extras] -docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +testing = ["importlib-resources (>=1.3)", "pep517", "packaging"] +docs = ["rst.linker", "sphinx"] [[package]] name = "iniconfig" @@ -404,16 +397,17 @@ python-versions = "*" [[package]] name = "isort" -version = "5.4.2" +version = "5.10.1" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.1,<4.0" [package.extras] -pipfile_deprecated_finder = ["pipreqs", "requirementslib", "tomlkit (>=0.5.3)"] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "jedi" @@ -488,48 +482,6 @@ traitlets = "*" [package.extras] test = ["async-generator", "ipykernel", "ipython", "mock", "pytest", "pytest-asyncio", "pytest-timeout"] -[[package]] -name = "jupyter-contrib-core" -version = "0.3.3" -description = "Common utilities for jupyter-contrib projects." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jupyter-core = "*" -notebook = ">=4.0" -tornado = "*" -traitlets = "*" - -[package.extras] -testing_utils = ["nose", "mock"] - -[[package]] -name = "jupyter-contrib-nbextensions" -version = "0.5.1" -description = "A collection of Jupyter nbextensions." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ipython-genutils = "*" -jupyter-contrib-core = ">=0.3.3" -jupyter-core = "*" -jupyter-highlight-selected-word = ">=0.1.1" -jupyter-latex-envs = ">=1.3.8" -jupyter-nbextensions-configurator = ">=0.4.0" -lxml = "*" -nbconvert = ">=4.2" -notebook = ">=4.0" -pyyaml = "*" -tornado = "*" -traitlets = ">=4.1" - -[package.extras] -test = ["nbformat", "nose", "pip", "requests", "mock"] - [[package]] name = "jupyter-core" version = "4.6.3" @@ -542,48 +494,6 @@ python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,!=3.4,>=2.7" pywin32 = {version = ">=1.0", markers = "sys_platform == \"win32\""} traitlets = "*" -[[package]] -name = "jupyter-highlight-selected-word" -version = "0.2.0" -description = "Jupyter notebook extension that enables highlighting every instance of the current word in the notebook." -category = "dev" -optional = false -python-versions = "*" - -[[package]] -name = "jupyter-latex-envs" -version = "1.4.6" -description = "Jupyter notebook extension which supports (some) LaTeX environments within markdown cells. Also provides support for labels and crossreferences, document wide numbering, bibliography, and more..." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -ipython = "*" -jupyter_core = "*" -nbconvert = "*" -notebook = ">=4.0" -traitlets = ">=4.1" - -[[package]] -name = "jupyter-nbextensions-configurator" -version = "0.4.1" -description = "jupyter serverextension providing configuration interfaces for nbextensions." -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -jupyter_contrib_core = ">=0.3.3" -jupyter_core = "*" -notebook = ">=4.0" -pyyaml = "*" -tornado = "*" -traitlets = "*" - -[package.extras] -test = ["jupyter-contrib-core", "nose", "requests", "selenium", "mock"] - [[package]] name = "kiwisolver" version = "1.2.0" @@ -604,20 +514,6 @@ python-versions = "*" six = "*" tornado = {version = "*", markers = "python_version > \"2.7\""} -[[package]] -name = "lxml" -version = "4.9.1" -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 = "markupsafe" version = "1.1.1" @@ -659,28 +555,23 @@ category = "dev" optional = false python-versions = "*" -[[package]] -name = "more-itertools" -version = "8.4.0" -description = "More routines for operating on iterables, beyond itertools" -category = "dev" -optional = false -python-versions = ">=3.5" - [[package]] name = "mypy" -version = "0.782" +version = "0.971" description = "Optional static typing for Python" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] -mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" -typing-extensions = ">=3.7.4" +mypy-extensions = ">=0.4.3" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typed-ast = {version = ">=1.4.0,<2", markers = "python_version < \"3.8\""} +typing-extensions = ">=3.10" [package.extras] +reports = ["lxml"] +python2 = ["typed-ast (>=1.4.0,<2)"] dmypy = ["psutil (>=4.0)"] [[package]] @@ -776,11 +667,11 @@ test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "re [[package]] name = "numpy" -version = "1.19.1" +version = "1.21.1" description = "NumPy is the fundamental package for array computing with Python." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "packaging" @@ -808,7 +699,7 @@ python-dateutil = ">=2.7.3" pytz = ">=2017.2" [package.extras] -test = ["pytest (>=4.0.2)", "pytest-xdist", "hypothesis (>=3.58)"] +test = ["hypothesis (>=3.58)", "pytest-xdist", "pytest (>=4.0.2)"] [[package]] name = "pandocfilters" @@ -827,15 +718,15 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.extras] -testing = ["docopt", "pytest (>=3.0.7)"] +testing = ["pytest (>=3.0.7)", "docopt"] [[package]] name = "pathspec" -version = "0.8.0" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pathtools" @@ -891,6 +782,18 @@ category = "dev" optional = false python-versions = ">=3.6" +[[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] +test = ["pytest (>=6)", "pytest-mock (>=3.6)", "pytest-cov (>=2.7)", "appdirs (==1.4.4)"] +docs = ["sphinx (>=4)", "sphinx-autodoc-typehints (>=1.12)", "proselint (>=0.10.2)", "furo (>=2021.7.5b38)"] + [[package]] name = "pluggy" version = "0.13.1" @@ -953,11 +856,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pycodestyle" -version = "2.6.0" +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.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycparser" @@ -969,18 +872,21 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydocstyle" -version = "5.0.2" +version = "6.1.1" description = "Python docstring style checker" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] snowballstemmer = "*" +[package.extras] +toml = ["toml"] + [[package]] name = "pyflakes" -version = "2.2.0" +version = "2.4.0" description = "passive checker of Python programs" category = "dev" optional = false @@ -1015,27 +921,25 @@ six = "*" [[package]] name = "pytest" -version = "6.0.1" +version = "7.1.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} -attrs = ">=17.4.0" +attrs = ">=19.2.0" colorama = {version = "*", markers = "sys_platform == \"win32\""} importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} iniconfig = "*" -more-itertools = ">=4.0.0" packaging = "*" -pluggy = ">=0.12,<1.0" +pluggy = ">=0.12,<2.0" py = ">=1.8.2" -toml = "*" +tomli = ">=1.0.0" [package.extras] -checkqa_mypy = ["mypy (==0.780)"] -testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"] +testing = ["xmlschema", "requests", "pygments (>=2.7.2)", "nose", "mock", "hypothesis (>=3.56)", "argcomplete"] [[package]] name = "pytest-cov" @@ -1127,14 +1031,6 @@ category = "dev" optional = false python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*" -[[package]] -name = "regex" -version = "2020.7.14" -description = "Alternative regular expression module, to replace re." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "requests" version = "2.26.0" @@ -1155,14 +1051,14 @@ use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] [[package]] name = "rope" -version = "0.17.0" +version = "1.1.1" description = "a python refactoring library..." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3" [package.extras] -dev = ["pytest"] +dev = ["pytest-timeout", "pytest", "build"] [[package]] name = "scikit-learn" @@ -1289,7 +1185,7 @@ python-versions = "*" sphinx = "*" [package.extras] -dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"] +dev = ["bump2version", "sphinxcontrib-httpdomain", "transifex-client"] [[package]] name = "sphinxcontrib-apidoc" @@ -1312,8 +1208,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-devhelp" @@ -1324,8 +1220,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-htmlhelp" @@ -1336,8 +1232,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] -test = ["pytest", "html5lib"] +test = ["html5lib", "pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-jsmath" @@ -1348,7 +1244,7 @@ optional = false python-versions = ">=3.5" [package.extras] -test = ["pytest", "flake8", "mypy"] +test = ["mypy", "flake8", "pytest"] [[package]] name = "sphinxcontrib-qthelp" @@ -1359,8 +1255,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "sphinxcontrib-serializinghtml" @@ -1371,8 +1267,8 @@ optional = false python-versions = ">=3.5" [package.extras] -lint = ["flake8", "mypy", "docutils-stubs"] test = ["pytest"] +lint = ["docutils-stubs", "mypy", "flake8"] [[package]] name = "terminado" @@ -1407,12 +1303,20 @@ optional = false python-versions = ">=3.5" [[package]] -name = "toml" -version = "0.10.1" -description = "Python Library for Tom's Obvious, Minimal Language" +name = "tokenize-rt" +version = "4.2.1" +description = "A wrapper around the stdlib `tokenize` which roundtrips." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6.1" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" [[package]] name = "tornado" @@ -1440,19 +1344,19 @@ test = ["pytest", "mock"] [[package]] name = "typed-ast" -version = "1.4.1" +version = "1.5.4" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "3.7.4.2" -description = "Backported and Experimental Type Hints for Python 3.5+" -category = "dev" +version = "4.3.0" +description = "Backported and Experimental Type Hints for Python 3.7+" +category = "main" optional = false -python-versions = "*" +python-versions = ">=3.7" [[package]] name = "urllib3" @@ -1514,23 +1418,19 @@ optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["jaraco.itertools", "func-timeout"] +testing = ["func-timeout", "jaraco.itertools"] +docs = ["rst.linker (>=1.9)", "jaraco.packaging (>=3.2)", "sphinx"] [metadata] lock-version = "1.1" -python-versions = "^3.6.1" -content-hash = "97a42af9b83f92b4f460425e0afb8ab9f1a2afb684b40f2059c3338baecf7c6d" +python-versions = "^3.7" +content-hash = "843fc9b6b16378437356bd7e739b22526e2f8a3c50b31df53c8886cee8b44771" [metadata.files] alabaster = [ {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"}, {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"}, ] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] appnope = [ {file = "appnope-0.1.0-py2.py3-none-any.whl", hash = "sha256:5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0"}, {file = "appnope-0.1.0.tar.gz", hash = "sha256:8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"}, @@ -1580,8 +1480,29 @@ backcall = [ {file = "backcall-0.2.0.tar.gz", hash = "sha256:5cbdbf27be5e7cfadb448baf0aa95508f91f2bbc6c6437cd9cd06e2a4c215e1e"}, ] black = [ - {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, - {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f586c26118bc6e714ec58c09df0157fe2d9ee195c764f630eb0d8e7ccce72e69"}, + {file = "black-22.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b270a168d69edb8b7ed32c193ef10fd27844e5c60852039599f9184460ce0807"}, + {file = "black-22.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6797f58943fceb1c461fb572edbe828d811e719c24e03375fd25170ada53825e"}, + {file = "black-22.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c85928b9d5f83b23cee7d0efcb310172412fbf7cb9d9ce963bd67fd141781def"}, + {file = "black-22.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:f6fe02afde060bbeef044af7996f335fbe90b039ccf3f5eb8f16df8b20f77666"}, + {file = "black-22.6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cfaf3895a9634e882bf9d2363fed5af8888802d670f58b279b0bece00e9a872d"}, + {file = "black-22.6.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94783f636bca89f11eb5d50437e8e17fbc6a929a628d82304c80fa9cd945f256"}, + {file = "black-22.6.0-cp36-cp36m-win_amd64.whl", hash = "sha256:2ea29072e954a4d55a2ff58971b83365eba5d3d357352a07a7a4df0d95f51c78"}, + {file = "black-22.6.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e439798f819d49ba1c0bd9664427a05aab79bfba777a6db94fd4e56fae0cb849"}, + {file = "black-22.6.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:187d96c5e713f441a5829e77120c269b6514418f4513a390b0499b0987f2ff1c"}, + {file = "black-22.6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:074458dc2f6e0d3dab7928d4417bb6957bb834434516f21514138437accdbe90"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a218d7e5856f91d20f04e931b6f16d15356db1c846ee55f01bac297a705ca24f"}, + {file = "black-22.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:568ac3c465b1c8b34b61cd7a4e349e93f91abf0f9371eda1cf87194663ab684e"}, + {file = "black-22.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6c1734ab264b8f7929cef8ae5f900b85d579e6cbfde09d7387da8f04771b51c6"}, + {file = "black-22.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9a3ac16efe9ec7d7381ddebcc022119794872abce99475345c5a61aa18c45ad"}, + {file = "black-22.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b9fd45787ba8aa3f5e0a0a98920c1012c884622c6c920dbe98dbd05bc7c70fbf"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7ba9be198ecca5031cd78745780d65a3f75a34b2ff9be5837045dce55db83d1c"}, + {file = "black-22.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a3db5b6409b96d9bd543323b23ef32a1a2b06416d525d27e0f67e74f1446c8f2"}, + {file = "black-22.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:560558527e52ce8afba936fcce93a7411ab40c7d5fe8c2463e279e843c0328ee"}, + {file = "black-22.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b154e6bbde1e79ea3260c4b40c0b7b3109ffcdf7bc4ebf8859169a6af72cd70b"}, + {file = "black-22.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:4af5bc0e1f96be5ae9bd7aaec219c901a94d6caa2484c21983d043371c733fc4"}, + {file = "black-22.6.0-py3-none-any.whl", hash = "sha256:ac609cf8ef5e7115ddd07d85d988d074ed00e10fbc3445aee393e70164a2219c"}, + {file = "black-22.6.0.tar.gz", hash = "sha256:6c6d39e28aed379aec40da1c65434c77d75e65bb59a1e1c283de545fb4e7c6c9"}, ] bleach = [ {file = "bleach-3.3.0-py2.py3-none-any.whl", hash = "sha256:6123ddc1052673e52bab52cdc955bcb57a015264a1c57d37bea2f6b817af0125"}, @@ -1626,48 +1547,64 @@ charset-normalizer = [ {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, ] click = [ - {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, - {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, + {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.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"}, {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"}, ] coverage = [ - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:40f70f81be4d34f8d491e55936904db5c527b0711b2a46513641a5729783c2e4"}, - {file = "coverage-5.2.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:675192fca634f0df69af3493a48224f211f8db4e84452b08d5fcebb9167adb01"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:2fcc8b58953d74d199a1a4d633df8146f0ac36c4e720b4a1997e9b6327af43a8"}, - {file = "coverage-5.2.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:64c4f340338c68c463f1b56e3f2f0423f7b17ba6c3febae80b81f0e093077f59"}, - {file = "coverage-5.2.1-cp27-cp27m-win32.whl", hash = "sha256:52f185ffd3291196dc1aae506b42e178a592b0b60a8610b108e6ad892cfc1bb3"}, - {file = "coverage-5.2.1-cp27-cp27m-win_amd64.whl", hash = "sha256:30bc103587e0d3df9e52cd9da1dd915265a22fad0b72afe54daf840c984b564f"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:9ea749fd447ce7fb1ac71f7616371f04054d969d412d37611716721931e36efd"}, - {file = "coverage-5.2.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:ce7866f29d3025b5b34c2e944e66ebef0d92e4a4f2463f7266daa03a1332a651"}, - {file = "coverage-5.2.1-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:4869ab1c1ed33953bb2433ce7b894a28d724b7aa76c19b11e2878034a4e4680b"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:a3ee9c793ffefe2944d3a2bd928a0e436cd0ac2d9e3723152d6fd5398838ce7d"}, - {file = "coverage-5.2.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28f42dc5172ebdc32622a2c3f7ead1b836cdbf253569ae5673f499e35db0bac3"}, - {file = "coverage-5.2.1-cp35-cp35m-win32.whl", hash = "sha256:e26c993bd4b220429d4ec8c1468eca445a4064a61c74ca08da7429af9bc53bb0"}, - {file = "coverage-5.2.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4186fc95c9febeab5681bc3248553d5ec8c2999b8424d4fc3a39c9cba5796962"}, - {file = "coverage-5.2.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:b360d8fd88d2bad01cb953d81fd2edd4be539df7bfec41e8753fe9f4456a5082"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:1adb6be0dcef0cf9434619d3b892772fdb48e793300f9d762e480e043bd8e716"}, - {file = "coverage-5.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:098a703d913be6fbd146a8c50cc76513d726b022d170e5e98dc56d958fd592fb"}, - {file = "coverage-5.2.1-cp36-cp36m-win32.whl", hash = "sha256:962c44070c281d86398aeb8f64e1bf37816a4dfc6f4c0f114756b14fc575621d"}, - {file = "coverage-5.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1ed2bdb27b4c9fc87058a1cb751c4df8752002143ed393899edb82b131e0546"}, - {file = "coverage-5.2.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:c890728a93fffd0407d7d37c1e6083ff3f9f211c83b4316fae3778417eab9811"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:538f2fd5eb64366f37c97fdb3077d665fa946d2b6d95447622292f38407f9258"}, - {file = "coverage-5.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:27ca5a2bc04d68f0776f2cdcb8bbd508bbe430a7bf9c02315cd05fb1d86d0034"}, - {file = "coverage-5.2.1-cp37-cp37m-win32.whl", hash = "sha256:aab75d99f3f2874733946a7648ce87a50019eb90baef931698f96b76b6769a46"}, - {file = "coverage-5.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c2ff24df02a125b7b346c4c9078c8936da06964cc2d276292c357d64378158f8"}, - {file = "coverage-5.2.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:304fbe451698373dc6653772c72c5d5e883a4aadaf20343592a7abb2e643dae0"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c96472b8ca5dc135fb0aa62f79b033f02aa434fb03a8b190600a5ae4102df1fd"}, - {file = "coverage-5.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:8505e614c983834239f865da2dd336dcf9d72776b951d5dfa5ac36b987726e1b"}, - {file = "coverage-5.2.1-cp38-cp38-win32.whl", hash = "sha256:700997b77cfab016533b3e7dbc03b71d33ee4df1d79f2463a318ca0263fc29dd"}, - {file = "coverage-5.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:46794c815e56f1431c66d81943fa90721bb858375fb36e5903697d5eef88627d"}, - {file = "coverage-5.2.1-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:16042dc7f8e632e0dcd5206a5095ebd18cb1d005f4c89694f7f8aafd96dd43a3"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:c1bbb628ed5192124889b51204de27c575b3ffc05a5a91307e7640eff1d48da4"}, - {file = "coverage-5.2.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:4f6428b55d2916a69f8d6453e48a505c07b2245653b0aa9f0dee38785939f5e4"}, - {file = "coverage-5.2.1-cp39-cp39-win32.whl", hash = "sha256:9e536783a5acee79a9b308be97d3952b662748c4037b6a24cbb339dc7ed8eb89"}, - {file = "coverage-5.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b8f58c7db64d8f27078cbf2a4391af6aa4e4767cc08b37555c4ae064b8558d9b"}, - {file = "coverage-5.2.1.tar.gz", hash = "sha256:a34cb28e0747ea15e82d13e14de606747e9e484fb28d63c999483f5d5188e89b"}, + {file = "coverage-6.4.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7b4da9bafad21ea45a714d3ea6f3e1679099e420c8741c74905b92ee9bfa7cc"}, + {file = "coverage-6.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fde17bc42e0716c94bf19d92e4c9f5a00c5feb401f5bc01101fdf2a8b7cacf60"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cdbb0d89923c80dbd435b9cf8bba0ff55585a3cdb28cbec65f376c041472c60d"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:67f9346aeebea54e845d29b487eb38ec95f2ecf3558a3cffb26ee3f0dcc3e760"}, + {file = "coverage-6.4.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42c499c14efd858b98c4e03595bf914089b98400d30789511577aa44607a1b74"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c35cca192ba700979d20ac43024a82b9b32a60da2f983bec6c0f5b84aead635c"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:9cc4f107009bca5a81caef2fca843dbec4215c05e917a59dec0c8db5cff1d2aa"}, + {file = "coverage-6.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f444627b3664b80d078c05fe6a850dd711beeb90d26731f11d492dcbadb6973"}, + {file = "coverage-6.4.4-cp310-cp310-win32.whl", hash = "sha256:66e6df3ac4659a435677d8cd40e8eb1ac7219345d27c41145991ee9bf4b806a0"}, + {file = "coverage-6.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:35ef1f8d8a7a275aa7410d2f2c60fa6443f4a64fae9be671ec0696a68525b875"}, + {file = "coverage-6.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c1328d0c2f194ffda30a45f11058c02410e679456276bfa0bbe0b0ee87225fac"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61b993f3998ee384935ee423c3d40894e93277f12482f6e777642a0141f55782"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d5dd4b8e9cd0deb60e6fcc7b0647cbc1da6c33b9e786f9c79721fd303994832f"}, + {file = "coverage-6.4.4-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7026f5afe0d1a933685d8f2169d7c2d2e624f6255fb584ca99ccca8c0e966fd7"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9c7b9b498eb0c0d48b4c2abc0e10c2d78912203f972e0e63e3c9dc21f15abdaa"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ee2b2fb6eb4ace35805f434e0f6409444e1466a47f620d1d5763a22600f0f892"}, + {file = "coverage-6.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ab066f5ab67059d1f1000b5e1aa8bbd75b6ed1fc0014559aea41a9eb66fc2ce0"}, + {file = "coverage-6.4.4-cp311-cp311-win32.whl", hash = "sha256:9d6e1f3185cbfd3d91ac77ea065d85d5215d3dfa45b191d14ddfcd952fa53796"}, + {file = "coverage-6.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:e3d3c4cc38b2882f9a15bafd30aec079582b819bec1b8afdbde8f7797008108a"}, + {file = "coverage-6.4.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a095aa0a996ea08b10580908e88fbaf81ecf798e923bbe64fb98d1807db3d68a"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef6f44409ab02e202b31a05dd6666797f9de2aa2b4b3534e9d450e42dea5e817"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4b7101938584d67e6f45f0015b60e24a95bf8dea19836b1709a80342e01b472f"}, + {file = "coverage-6.4.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:14a32ec68d721c3d714d9b105c7acf8e0f8a4f4734c811eda75ff3718570b5e3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6a864733b22d3081749450466ac80698fe39c91cb6849b2ef8752fd7482011f3"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:08002f9251f51afdcc5e3adf5d5d66bb490ae893d9e21359b085f0e03390a820"}, + {file = "coverage-6.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a3b2752de32c455f2521a51bd3ffb53c5b3ae92736afde67ce83477f5c1dd928"}, + {file = "coverage-6.4.4-cp37-cp37m-win32.whl", hash = "sha256:f855b39e4f75abd0dfbcf74a82e84ae3fc260d523fcb3532786bcbbcb158322c"}, + {file = "coverage-6.4.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ee6ae6bbcac0786807295e9687169fba80cb0617852b2fa118a99667e8e6815d"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:564cd0f5b5470094df06fab676c6d77547abfdcb09b6c29c8a97c41ad03b103c"}, + {file = "coverage-6.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cbbb0e4cd8ddcd5ef47641cfac97d8473ab6b132dd9a46bacb18872828031685"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6113e4df2fa73b80f77663445be6d567913fb3b82a86ceb64e44ae0e4b695de1"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8d032bfc562a52318ae05047a6eb801ff31ccee172dc0d2504614e911d8fa83e"}, + {file = "coverage-6.4.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e431e305a1f3126477abe9a184624a85308da8edf8486a863601d58419d26ffa"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:cf2afe83a53f77aec067033199797832617890e15bed42f4a1a93ea24794ae3e"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:783bc7c4ee524039ca13b6d9b4186a67f8e63d91342c713e88c1865a38d0892a"}, + {file = "coverage-6.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:ff934ced84054b9018665ca3967fc48e1ac99e811f6cc99ea65978e1d384454b"}, + {file = "coverage-6.4.4-cp38-cp38-win32.whl", hash = "sha256:e1fabd473566fce2cf18ea41171d92814e4ef1495e04471786cbc943b89a3781"}, + {file = "coverage-6.4.4-cp38-cp38-win_amd64.whl", hash = "sha256:4179502f210ebed3ccfe2f78bf8e2d59e50b297b598b100d6c6e3341053066a2"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c0b9e9b572893cdb0a00e66cf961a238f8d870d4e1dc8e679eb8bdc2eb1b86"}, + {file = "coverage-6.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fc600f6ec19b273da1d85817eda339fb46ce9eef3e89f220055d8696e0a06908"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a98d6bf6d4ca5c07a600c7b4e0c5350cd483c85c736c522b786be90ea5bac4f"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01778769097dbd705a24e221f42be885c544bb91251747a8a3efdec6eb4788f2"}, + {file = "coverage-6.4.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dfa0b97eb904255e2ab24166071b27408f1f69c8fbda58e9c0972804851e0558"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:fcbe3d9a53e013f8ab88734d7e517eb2cd06b7e689bedf22c0eb68db5e4a0a19"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:15e38d853ee224e92ccc9a851457fb1e1f12d7a5df5ae44544ce7863691c7a0d"}, + {file = "coverage-6.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6913dddee2deff8ab2512639c5168c3e80b3ebb0f818fed22048ee46f735351a"}, + {file = "coverage-6.4.4-cp39-cp39-win32.whl", hash = "sha256:354df19fefd03b9a13132fa6643527ef7905712109d9c1c1903f2133d3a4e145"}, + {file = "coverage-6.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:1238b08f3576201ebf41f7c20bf59baa0d05da941b123c6656e42cdb668e9827"}, + {file = "coverage-6.4.4-pp36.pp37.pp38-none-any.whl", hash = "sha256:f67cf9f406cf0d2f08a3515ce2db5b82625a7257f88aad87904674def6ddaec1"}, + {file = "coverage-6.4.4.tar.gz", hash = "sha256:e16c45b726acb780e1e6f88b286d3c10b3914ab03438f32117c4aa52d7f30d58"}, ] cycler = [ {file = "cycler-0.10.0-py2.py3-none-any.whl", hash = "sha256:1d8a5ae1ff6c5cf9b93e8811e581232ad8920aeec647c37316ceac982b08cb2d"}, @@ -1689,29 +1626,25 @@ entrypoints = [ {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] +exceptiongroup = [ + {file = "exceptiongroup-1.0.0rc8-py3-none-any.whl", hash = "sha256:ab0a968e1ef769e55d9a596f4a89f7be9ffedbc9fdefdb77cc68cf5c33ce1035"}, + {file = "exceptiongroup-1.0.0rc8.tar.gz", hash = "sha256:6990c24f06b8d33c8065cfe43e5e8a4bfa384e0358be036af9cc60b6321bd11a"}, +] flake8 = [ - {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"}, - {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"}, + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] flake8-bugbear = [ {file = "flake8-bugbear-20.1.4.tar.gz", hash = "sha256:bd02e4b009fb153fe6072c31c52aeab5b133d508095befb2ffcf3b41c4823162"}, {file = "flake8_bugbear-20.1.4-py36.py37.py38-none-any.whl", hash = "sha256:a3ddc03ec28ba2296fc6f89444d1c946a6b76460f859795b35b77d4920a51b63"}, ] -flake8-docstrings = [ - {file = "flake8-docstrings-1.5.0.tar.gz", hash = "sha256:3d5a31c7ec6b7367ea6506a87ec293b94a0a46c0bce2bb4975b7f1d09b6f3717"}, - {file = "flake8_docstrings-1.5.0-py2.py3-none-any.whl", hash = "sha256:a256ba91bc52307bef1de59e2a009c3cf61c3d0952dbe035d6ff7208940c2edc"}, -] -flake8-import-order = [ - {file = "flake8-import-order-0.18.1.tar.gz", hash = "sha256:a28dc39545ea4606c1ac3c24e9d05c849c6e5444a50fb7e9cdd430fc94de6e92"}, - {file = "flake8_import_order-0.18.1-py2.py3-none-any.whl", hash = "sha256:90a80e46886259b9c396b578d75c749801a41ee969a235e163cfe1be7afd2543"}, -] 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"}, ] hypothesis = [ - {file = "hypothesis-5.26.0-py3-none-any.whl", hash = "sha256:88260b37437f4ae311822589bdb798a15c3def70db1839d86fa0a3ff4b63d564"}, - {file = "hypothesis-5.26.0.tar.gz", hash = "sha256:54f3ce8d7146cb59eef419a26d5edfdf4e8518615b9df8c7e1f5392a2c5c9c36"}, + {file = "hypothesis-6.54.4-py3-none-any.whl", hash = "sha256:469125235b4fdd6b6a4b6a6fa13e528d3e8de4dbd3bd9236b03323fefa3a9b32"}, + {file = "hypothesis-6.54.4.tar.gz", hash = "sha256:ee42fe4d2ff96c49910085780d6b8f34cbcf4c44427616e22833869d451116bb"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, @@ -1742,8 +1675,8 @@ ipython-genutils = [ {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, ] isort = [ - {file = "isort-5.4.2-py3-none-any.whl", hash = "sha256:60a1b97e33f61243d12647aaaa3e6cc6778f5eb9f42997650f1cc975b6008750"}, - {file = "isort-5.4.2.tar.gz", hash = "sha256:d488ba1c5a2db721669cc180180d5acf84ebdc5af7827f7aaeaa75f73cf0e2b8"}, + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] jedi = [ {file = "jedi-0.17.2-py2.py3-none-any.whl", hash = "sha256:98cc583fa0f2f8304968199b01b6b4b94f469a1f4a74c1560506ca2a211378b5"}, @@ -1765,28 +1698,10 @@ jupyter-client = [ {file = "jupyter_client-6.1.6-py3-none-any.whl", hash = "sha256:7ad9aa91505786420d77edc5f9fb170d51050c007338ba8d196f603223fd3b3a"}, {file = "jupyter_client-6.1.6.tar.gz", hash = "sha256:b360f8d4638bc577a4656e93f86298db755f915098dc763f6fc05da0c5d7a595"}, ] -jupyter-contrib-core = [ - {file = "jupyter_contrib_core-0.3.3-py2.py3-none-any.whl", hash = "sha256:1ec81e275a8f5858d56b0c4c6cd85335aa8e915001b8657fe51c620c3cdde50f"}, - {file = "jupyter_contrib_core-0.3.3.tar.gz", hash = "sha256:e65bc0e932ff31801003cef160a4665f2812efe26a53801925a634735e9a5794"}, -] -jupyter-contrib-nbextensions = [ - {file = "jupyter_contrib_nbextensions-0.5.1-py2.py3-none-any.whl", hash = "sha256:2c071f0aa208c569666f656bdc0f66906ca493cf9f06f46db6350db11030ff40"}, - {file = "jupyter_contrib_nbextensions-0.5.1.tar.gz", hash = "sha256:eecd28ecc2fc410226c0a3d4932ed2fac4860ccf8d9e9b1b29548835a35b22ab"}, -] jupyter-core = [ {file = "jupyter_core-4.6.3-py2.py3-none-any.whl", hash = "sha256:a4ee613c060fe5697d913416fc9d553599c05e4492d58fac1192c9a6844abb21"}, {file = "jupyter_core-4.6.3.tar.gz", hash = "sha256:394fd5dd787e7c8861741880bdf8a00ce39f95de5d18e579c74b882522219e7e"}, ] -jupyter-highlight-selected-word = [ - {file = "jupyter_highlight_selected_word-0.2.0-py2.py3-none-any.whl", hash = "sha256:9545dfa9cb057eebe3a5795604dcd3a5294ea18637e553f61a0b67c1b5903c58"}, - {file = "jupyter_highlight_selected_word-0.2.0.tar.gz", hash = "sha256:9fa740424859a807950ca08d2bfd28a35154cd32dd6d50ac4e0950022adc0e7b"}, -] -jupyter-latex-envs = [ - {file = "jupyter_latex_envs-1.4.6.tar.gz", hash = "sha256:070a31eb2dc488bba983915879a7c2939247bf5c3b669b398bdb36a9b5343872"}, -] -jupyter-nbextensions-configurator = [ - {file = "jupyter_nbextensions_configurator-0.4.1.tar.gz", hash = "sha256:e5e86b5d9d898e1ffb30ebb08e4ad8696999f798fef3ff3262d7b999076e4e83"}, -] kiwisolver = [ {file = "kiwisolver-1.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:443c2320520eda0a5b930b2725b26f6175ca4453c61f739fef7a5847bd262f74"}, {file = "kiwisolver-1.2.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:efcf3397ae1e3c3a4a0a0636542bcad5adad3b1dd3e8e629d0b6e201347176c8"}, @@ -1811,78 +1726,6 @@ kiwisolver = [ livereload = [ {file = "livereload-2.6.2.tar.gz", hash = "sha256:d1eddcb5c5eb8d2ca1fa1f750e580da624c0f7fcb734aa5780dc81b7dcbd89be"}, ] -lxml = [ - {file = "lxml-4.9.1-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:98cafc618614d72b02185ac583c6f7796202062c41d2eeecdf07820bad3295ed"}, - {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c62e8dd9754b7debda0c5ba59d34509c4688f853588d75b53c3791983faa96fc"}, - {file = "lxml-4.9.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:21fb3d24ab430fc538a96e9fbb9b150029914805d551deeac7d7822f64631dfc"}, - {file = "lxml-4.9.1-cp27-cp27m-win32.whl", hash = "sha256:86e92728ef3fc842c50a5cb1d5ba2bc66db7da08a7af53fb3da79e202d1b2cd3"}, - {file = "lxml-4.9.1-cp27-cp27m-win_amd64.whl", hash = "sha256:4cfbe42c686f33944e12f45a27d25a492cc0e43e1dc1da5d6a87cbcaf2e95627"}, - {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:dad7b164905d3e534883281c050180afcf1e230c3d4a54e8038aa5cfcf312b84"}, - {file = "lxml-4.9.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a614e4afed58c14254e67862456d212c4dcceebab2eaa44d627c2ca04bf86837"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f9ced82717c7ec65a67667bb05865ffe38af0e835cdd78728f1209c8fffe0cad"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:d9fc0bf3ff86c17348dfc5d322f627d78273eba545db865c3cd14b3f19e57fa5"}, - {file = "lxml-4.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e5f66bdf0976ec667fc4594d2812a00b07ed14d1b44259d19a41ae3fff99f2b8"}, - {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fe17d10b97fdf58155f858606bddb4e037b805a60ae023c009f760d8361a4eb8"}, - {file = "lxml-4.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8caf4d16b31961e964c62194ea3e26a0e9561cdf72eecb1781458b67ec83423d"}, - {file = "lxml-4.9.1-cp310-cp310-win32.whl", hash = "sha256:4780677767dd52b99f0af1f123bc2c22873d30b474aa0e2fc3fe5e02217687c7"}, - {file = "lxml-4.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:b122a188cd292c4d2fcd78d04f863b789ef43aa129b233d7c9004de08693728b"}, - {file = "lxml-4.9.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:be9eb06489bc975c38706902cbc6888f39e946b81383abc2838d186f0e8b6a9d"}, - {file = "lxml-4.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:f1be258c4d3dc609e654a1dc59d37b17d7fef05df912c01fc2e15eb43a9735f3"}, - {file = "lxml-4.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:927a9dd016d6033bc12e0bf5dee1dde140235fc8d0d51099353c76081c03dc29"}, - {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9232b09f5efee6a495a99ae6824881940d6447debe272ea400c02e3b68aad85d"}, - {file = "lxml-4.9.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:04da965dfebb5dac2619cb90fcf93efdb35b3c6994fea58a157a834f2f94b318"}, - {file = "lxml-4.9.1-cp35-cp35m-win32.whl", hash = "sha256:4d5bae0a37af799207140652a700f21a85946f107a199bcb06720b13a4f1f0b7"}, - {file = "lxml-4.9.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4878e667ebabe9b65e785ac8da4d48886fe81193a84bbe49f12acff8f7a383a4"}, - {file = "lxml-4.9.1-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:1355755b62c28950f9ce123c7a41460ed9743c699905cbe664a5bcc5c9c7c7fb"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:bcaa1c495ce623966d9fc8a187da80082334236a2a1c7e141763ffaf7a405067"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6eafc048ea3f1b3c136c71a86db393be36b5b3d9c87b1c25204e7d397cee9536"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:13c90064b224e10c14dcdf8086688d3f0e612db53766e7478d7754703295c7c8"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:206a51077773c6c5d2ce1991327cda719063a47adc02bd703c56a662cdb6c58b"}, - {file = "lxml-4.9.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e8f0c9d65da595cfe91713bc1222af9ecabd37971762cb830dea2fc3b3bb2acf"}, - {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8f0a4d179c9a941eb80c3a63cdb495e539e064f8054230844dcf2fcb812b71d3"}, - {file = "lxml-4.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:830c88747dce8a3e7525defa68afd742b4580df6aa2fdd6f0855481e3994d391"}, - {file = "lxml-4.9.1-cp36-cp36m-win32.whl", hash = "sha256:1e1cf47774373777936c5aabad489fef7b1c087dcd1f426b621fda9dcc12994e"}, - {file = "lxml-4.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:5974895115737a74a00b321e339b9c3f45c20275d226398ae79ac008d908bff7"}, - {file = "lxml-4.9.1-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:1423631e3d51008871299525b541413c9b6c6423593e89f9c4cfbe8460afc0a2"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:2aaf6a0a6465d39b5ca69688fce82d20088c1838534982996ec46633dc7ad6cc"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:9f36de4cd0c262dd9927886cc2305aa3f2210db437aa4fed3fb4940b8bf4592c"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae06c1e4bc60ee076292e582a7512f304abdf6c70db59b56745cca1684f875a4"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:57e4d637258703d14171b54203fd6822fda218c6c2658a7d30816b10995f29f3"}, - {file = "lxml-4.9.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d279033bf614953c3fc4a0aa9ac33a21e8044ca72d4fa8b9273fe75359d5cca"}, - {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a60f90bba4c37962cbf210f0188ecca87daafdf60271f4c6948606e4dabf8785"}, - {file = "lxml-4.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6ca2264f341dd81e41f3fffecec6e446aa2121e0b8d026fb5130e02de1402785"}, - {file = "lxml-4.9.1-cp37-cp37m-win32.whl", hash = "sha256:27e590352c76156f50f538dbcebd1925317a0f70540f7dc8c97d2931c595783a"}, - {file = "lxml-4.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:eea5d6443b093e1545ad0210e6cf27f920482bfcf5c77cdc8596aec73523bb7e"}, - {file = "lxml-4.9.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:f05251bbc2145349b8d0b77c0d4e5f3b228418807b1ee27cefb11f69ed3d233b"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:487c8e61d7acc50b8be82bda8c8d21d20e133c3cbf41bd8ad7eb1aaeb3f07c97"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d1a92d8e90b286d491e5626af53afef2ba04da33e82e30744795c71880eaa21"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:b570da8cd0012f4af9fa76a5635cd31f707473e65a5a335b186069d5c7121ff2"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ef87fca280fb15342726bd5f980f6faf8b84a5287fcc2d4962ea8af88b35130"}, - {file = "lxml-4.9.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:93e414e3206779ef41e5ff2448067213febf260ba747fc65389a3ddaa3fb8715"}, - {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6653071f4f9bac46fbc30f3c7838b0e9063ee335908c5d61fb7a4a86c8fd2036"}, - {file = "lxml-4.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:32a73c53783becdb7eaf75a2a1525ea8e49379fb7248c3eeefb9412123536387"}, - {file = "lxml-4.9.1-cp38-cp38-win32.whl", hash = "sha256:1a7c59c6ffd6ef5db362b798f350e24ab2cfa5700d53ac6681918f314a4d3b94"}, - {file = "lxml-4.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:1436cf0063bba7888e43f1ba8d58824f085410ea2025befe81150aceb123e345"}, - {file = "lxml-4.9.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:4beea0f31491bc086991b97517b9683e5cfb369205dac0148ef685ac12a20a67"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41fb58868b816c202e8881fd0f179a4644ce6e7cbbb248ef0283a34b73ec73bb"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:bd34f6d1810d9354dc7e35158aa6cc33456be7706df4420819af6ed966e85448"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:edffbe3c510d8f4bf8640e02ca019e48a9b72357318383ca60e3330c23aaffc7"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d949f53ad4fc7cf02c44d6678e7ff05ec5f5552b235b9e136bd52e9bf730b91"}, - {file = "lxml-4.9.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:079b68f197c796e42aa80b1f739f058dcee796dc725cc9a1be0cdb08fc45b000"}, - {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9c3a88d20e4fe4a2a4a84bf439a5ac9c9aba400b85244c63a1ab7088f85d9d25"}, - {file = "lxml-4.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:4e285b5f2bf321fc0857b491b5028c5f276ec0c873b985d58d7748ece1d770dd"}, - {file = "lxml-4.9.1-cp39-cp39-win32.whl", hash = "sha256:ef72013e20dd5ba86a8ae1aed7f56f31d3374189aa8b433e7b12ad182c0d2dfb"}, - {file = "lxml-4.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:10d2017f9150248563bb579cd0d07c61c58da85c922b780060dcc9a3aa9f432d"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0538747a9d7827ce3e16a8fdd201a99e661c7dee3c96c885d8ecba3c35d1032c"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0645e934e940107e2fdbe7c5b6fb8ec6232444260752598bc4d09511bd056c0b"}, - {file = "lxml-4.9.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6daa662aba22ef3258934105be2dd9afa5bb45748f4f702a3b39a5bf53a1f4dc"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:603a464c2e67d8a546ddaa206d98e3246e5db05594b97db844c2f0a1af37cf5b"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c4b2e0559b68455c085fb0f6178e9752c4be3bba104d6e881eb5573b399d1eb2"}, - {file = "lxml-4.9.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:0f3f0059891d3254c7b5fb935330d6db38d6519ecd238ca4fce93c234b4a0f73"}, - {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:c852b1530083a620cb0de5f3cd6826f19862bafeaf77586f1aef326e49d95f0c"}, - {file = "lxml-4.9.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:287605bede6bd36e930577c5925fcea17cb30453d96a7b4c63c14a257118dbb9"}, - {file = "lxml-4.9.1.tar.gz", hash = "sha256:fe749b052bb7233fe5d072fcb549221a8cb1a16725c47c37e42b0b9cb3ff2c3f"}, -] markupsafe = [ {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"}, {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"}, @@ -1974,25 +1817,30 @@ mistune = [ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, ] -more-itertools = [ - {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"}, - {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"}, -] mypy = [ - {file = "mypy-0.782-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c"}, - {file = "mypy-0.782-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e"}, - {file = "mypy-0.782-cp35-cp35m-win_amd64.whl", hash = "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d"}, - {file = "mypy-0.782-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd"}, - {file = "mypy-0.782-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"}, - {file = "mypy-0.782-cp36-cp36m-win_amd64.whl", hash = "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406"}, - {file = "mypy-0.782-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86"}, - {file = "mypy-0.782-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707"}, - {file = "mypy-0.782-cp37-cp37m-win_amd64.whl", hash = "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308"}, - {file = "mypy-0.782-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc"}, - {file = "mypy-0.782-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea"}, - {file = "mypy-0.782-cp38-cp38-win_amd64.whl", hash = "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b"}, - {file = "mypy-0.782-py3-none-any.whl", hash = "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d"}, - {file = "mypy-0.782.tar.gz", hash = "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c"}, + {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, + {file = "mypy-0.971-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:98e02d56ebe93981c41211c05adb630d1d26c14195d04d95e49cd97dbc046dc5"}, + {file = "mypy-0.971-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:19830b7dba7d5356d3e26e2427a2ec91c994cd92d983142cbd025ebe81d69cf3"}, + {file = "mypy-0.971-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:02ef476f6dcb86e6f502ae39a16b93285fef97e7f1ff22932b657d1ef1f28655"}, + {file = "mypy-0.971-cp310-cp310-win_amd64.whl", hash = "sha256:25c5750ba5609a0c7550b73a33deb314ecfb559c350bb050b655505e8aed4103"}, + {file = "mypy-0.971-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d3348e7eb2eea2472db611486846742d5d52d1290576de99d59edeb7cd4a42ca"}, + {file = "mypy-0.971-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3fa7a477b9900be9b7dd4bab30a12759e5abe9586574ceb944bc29cddf8f0417"}, + {file = "mypy-0.971-cp36-cp36m-win_amd64.whl", hash = "sha256:2ad53cf9c3adc43cf3bea0a7d01a2f2e86db9fe7596dfecb4496a5dda63cbb09"}, + {file = "mypy-0.971-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:855048b6feb6dfe09d3353466004490b1872887150c5bb5caad7838b57328cc8"}, + {file = "mypy-0.971-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:23488a14a83bca6e54402c2e6435467a4138785df93ec85aeff64c6170077fb0"}, + {file = "mypy-0.971-cp37-cp37m-win_amd64.whl", hash = "sha256:4b21e5b1a70dfb972490035128f305c39bc4bc253f34e96a4adf9127cf943eb2"}, + {file = "mypy-0.971-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:9796a2ba7b4b538649caa5cecd398d873f4022ed2333ffde58eaf604c4d2cb27"}, + {file = "mypy-0.971-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5a361d92635ad4ada1b1b2d3630fc2f53f2127d51cf2def9db83cba32e47c856"}, + {file = "mypy-0.971-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b793b899f7cf563b1e7044a5c97361196b938e92f0a4343a5d27966a53d2ec71"}, + {file = "mypy-0.971-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d1ea5d12c8e2d266b5fb8c7a5d2e9c0219fedfeb493b7ed60cd350322384ac27"}, + {file = "mypy-0.971-cp38-cp38-win_amd64.whl", hash = "sha256:23c7ff43fff4b0df93a186581885c8512bc50fc4d4910e0f838e35d6bb6b5e58"}, + {file = "mypy-0.971-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1f7656b69974a6933e987ee8ffb951d836272d6c0f81d727f1d0e2696074d9e6"}, + {file = "mypy-0.971-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d2022bfadb7a5c2ef410d6a7c9763188afdb7f3533f22a0a32be10d571ee4bbe"}, + {file = "mypy-0.971-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef943c72a786b0f8d90fd76e9b39ce81fb7171172daf84bf43eaf937e9f220a9"}, + {file = "mypy-0.971-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d744f72eb39f69312bc6c2abf8ff6656973120e2eb3f3ec4f758ed47e414a4bf"}, + {file = "mypy-0.971-cp39-cp39-win_amd64.whl", hash = "sha256:77a514ea15d3007d33a9e2157b0ba9c267496acf12a7f2b9b9f8446337aac5b0"}, + {file = "mypy-0.971-py3-none-any.whl", hash = "sha256:0d054ef16b071149917085f51f89555a576e2618d5d9dd70bd6eea6410af3ac9"}, + {file = "mypy-0.971.tar.gz", hash = "sha256:40b0f21484238269ae6a57200c807d80debc6459d444c0489a102d7c6a75fa56"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, @@ -2015,32 +1863,34 @@ notebook = [ {file = "notebook-6.4.10.tar.gz", hash = "sha256:2408a76bc6289283a8eecfca67e298ec83c67db51a4c2e1b713dd180bb39e90e"}, ] numpy = [ - {file = "numpy-1.19.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1cca51512299841bf69add3b75361779962f9cee7d9ee3bb446d5982e925b69"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c9591886fc9cbe5532d5df85cb8e0cc3b44ba8ce4367bd4cf1b93dc19713da72"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:cf1347450c0b7644ea142712619533553f02ef23f92f781312f6a3553d031fc7"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed8a311493cf5480a2ebc597d1e177231984c818a86875126cfd004241a73c3e"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3673c8b2b29077f1b7b3a848794f8e11f401ba0b71c49fbd26fb40b71788b132"}, - {file = "numpy-1.19.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:56ef7f56470c24bb67fb43dae442e946a6ce172f97c69f8d067ff8550cf782ff"}, - {file = "numpy-1.19.1-cp36-cp36m-win32.whl", hash = "sha256:aaf42a04b472d12515debc621c31cf16c215e332242e7a9f56403d814c744624"}, - {file = "numpy-1.19.1-cp36-cp36m-win_amd64.whl", hash = "sha256:082f8d4dd69b6b688f64f509b91d482362124986d98dc7dc5f5e9f9b9c3bb983"}, - {file = "numpy-1.19.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e4f6d3c53911a9d103d8ec9518190e52a8b945bab021745af4939cfc7c0d4a9e"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:5b6885c12784a27e957294b60f97e8b5b4174c7504665333c5e94fbf41ae5d6a"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:1bc0145999e8cb8aed9d4e65dd8b139adf1919e521177f198529687dbf613065"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:5a936fd51049541d86ccdeef2833cc89a18e4d3808fe58a8abeb802665c5af93"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:ef71a1d4fd4858596ae80ad1ec76404ad29701f8ca7cdcebc50300178db14dfc"}, - {file = "numpy-1.19.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b9792b0ac0130b277536ab8944e7b754c69560dac0415dd4b2dbd16b902c8954"}, - {file = "numpy-1.19.1-cp37-cp37m-win32.whl", hash = "sha256:b12e639378c741add21fbffd16ba5ad25c0a1a17cf2b6fe4288feeb65144f35b"}, - {file = "numpy-1.19.1-cp37-cp37m-win_amd64.whl", hash = "sha256:8343bf67c72e09cfabfab55ad4a43ce3f6bf6e6ced7acf70f45ded9ebb425055"}, - {file = "numpy-1.19.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e45f8e981a0ab47103181773cc0a54e650b2aef8c7b6cd07405d0fa8d869444a"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:667c07063940e934287993366ad5f56766bc009017b4a0fe91dbd07960d0aba7"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:480fdd4dbda4dd6b638d3863da3be82873bba6d32d1fc12ea1b8486ac7b8d129"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:935c27ae2760c21cd7354402546f6be21d3d0c806fffe967f745d5f2de5005a7"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:309cbcfaa103fc9a33ec16d2d62569d541b79f828c382556ff072442226d1968"}, - {file = "numpy-1.19.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7ed448ff4eaffeb01094959b19cbaf998ecdee9ef9932381420d514e446601cd"}, - {file = "numpy-1.19.1-cp38-cp38-win32.whl", hash = "sha256:de8b4a9b56255797cbddb93281ed92acbc510fb7b15df3f01bd28f46ebc4edae"}, - {file = "numpy-1.19.1-cp38-cp38-win_amd64.whl", hash = "sha256:92feb989b47f83ebef246adabc7ff3b9a59ac30601c3f6819f8913458610bdcc"}, - {file = "numpy-1.19.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:e1b1dc0372f530f26a03578ac75d5e51b3868b9b76cd2facba4c9ee0eb252ab1"}, - {file = "numpy-1.19.1.zip", hash = "sha256:b8456987b637232602ceb4d663cb34106f7eb780e247d51a260b84760fd8f491"}, + {file = "numpy-1.21.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38e8648f9449a549a7dfe8d8755a5979b45b3538520d1e735637ef28e8c2dc50"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd7d7409fa643a91d0a05c7554dd68aa9c9bb16e186f6ccfe40d6e003156e33a"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a75b4498b1e93d8b700282dc8e655b8bd559c0904b3910b144646dbbbc03e062"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1412aa0aec3e00bc23fbb8664d76552b4efde98fb71f60737c83efbac24112f1"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e46ceaff65609b5399163de5893d8f2a82d3c77d5e56d976c8b5fb01faa6b671"}, + {file = "numpy-1.21.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:c6a2324085dd52f96498419ba95b5777e40b6bcbc20088fddb9e8cbb58885e8e"}, + {file = "numpy-1.21.1-cp37-cp37m-win32.whl", hash = "sha256:73101b2a1fef16602696d133db402a7e7586654682244344b8329cdcbbb82172"}, + {file = "numpy-1.21.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7a708a79c9a9d26904d1cca8d383bf869edf6f8e7650d85dbc77b041e8c5a0f8"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95b995d0c413f5d0428b3f880e8fe1660ff9396dcd1f9eedbc311f37b5652e16"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:635e6bd31c9fb3d475c8f44a089569070d10a9ef18ed13738b03049280281267"}, + {file = "numpy-1.21.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4a3d5fb89bfe21be2ef47c0614b9c9c707b7362386c9a3ff1feae63e0267ccb6"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a326af80e86d0e9ce92bcc1e65c8ff88297de4fa14ee936cb2293d414c9ec63"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:791492091744b0fe390a6ce85cc1bf5149968ac7d5f0477288f78c89b385d9af"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0318c465786c1f63ac05d7c4dbcecd4d2d7e13f0959b01b534ea1e92202235c5"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9a513bd9c1551894ee3d31369f9b07460ef223694098cf27d399513415855b68"}, + {file = "numpy-1.21.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:91c6f5fc58df1e0a3cc0c3a717bb3308ff850abdaa6d2d802573ee2b11f674a8"}, + {file = "numpy-1.21.1-cp38-cp38-win32.whl", hash = "sha256:978010b68e17150db8765355d1ccdd450f9fc916824e8c4e35ee620590e234cd"}, + {file = "numpy-1.21.1-cp38-cp38-win_amd64.whl", hash = "sha256:9749a40a5b22333467f02fe11edc98f022133ee1bfa8ab99bda5e5437b831214"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d7a4aeac3b94af92a9373d6e77b37691b86411f9745190d2c351f410ab3a791f"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d9e7912a56108aba9b31df688a4c4f5cb0d9d3787386b87d504762b6754fbb1b"}, + {file = "numpy-1.21.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:25b40b98ebdd272bc3020935427a4530b7d60dfbe1ab9381a39147834e985eac"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8a92c5aea763d14ba9d6475803fc7904bda7decc2a0a68153f587ad82941fec1"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05a0f648eb28bae4bcb204e6fd14603de2908de982e761a2fc78efe0f19e96e1"}, + {file = "numpy-1.21.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f01f28075a92eede918b965e86e8f0ba7b7797a95aa8d35e1cc8821f5fc3ad6a"}, + {file = "numpy-1.21.1-cp39-cp39-win32.whl", hash = "sha256:88c0b89ad1cc24a5efbb99ff9ab5db0f9a86e9cc50240177a571fbe9c2860ac2"}, + {file = "numpy-1.21.1-cp39-cp39-win_amd64.whl", hash = "sha256:01721eefe70544d548425a07c80be8377096a54118070b8a62476866d5208e33"}, + {file = "numpy-1.21.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d4d1de6e6fb3d28781c73fbde702ac97f03d79e4ffd6598b880b2d95d62ead4"}, + {file = "numpy-1.21.1.zip", hash = "sha256:dff4af63638afcc57a3dfb9e4b26d434a7a602d225b42d746ea7fe2edf1342fd"}, ] packaging = [ {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, @@ -2072,8 +1922,8 @@ parso = [ {file = "parso-0.7.1.tar.gz", hash = "sha256:caba44724b994a8a5e086460bb212abc5a8bc46951bf4a9a1210745953622eb9"}, ] pathspec = [ - {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"}, - {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pathtools = [ {file = "pathtools-0.1.2.tar.gz", hash = "sha256:7c35c5421a39bb82e58018febd90e3b6e5db34c5443aaaf742b3f33d4655f1c0"}, @@ -2149,6 +1999,10 @@ pillow = [ {file = "Pillow-8.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ce651ca46d0202c302a535d3047c55a0131a720cf554a578fc1b8a2aff0e7d96"}, {file = "Pillow-8.3.2.tar.gz", hash = "sha256:dde3f3ed8d00c72631bc19cbfff8ad3b6215062a5eed402381ad365f82f0c18c"}, ] +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-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"}, {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, @@ -2173,20 +2027,20 @@ py = [ {file = "py-1.10.0.tar.gz", hash = "sha256:21b81bda15b66ef5e1a777a21c4dcd9c20ad3efd0b3f817e7a809035269e1bd3"}, ] pycodestyle = [ - {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"}, - {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"}, + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] pycparser = [ {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, ] pydocstyle = [ - {file = "pydocstyle-5.0.2-py3-none-any.whl", hash = "sha256:da7831660b7355307b32778c4a0dbfb137d89254ef31a2b2978f50fc0b4d7586"}, - {file = "pydocstyle-5.0.2.tar.gz", hash = "sha256:f4f5d210610c2d153fae39093d44224c17429e2ad7da12a8b419aba5c2f614b5"}, + {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, + {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ - {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"}, - {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"}, + {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.7.4-py3-none-any.whl", hash = "sha256:bc9591213a8f0e0ca1a5e68a479b4887fdc3e75d0774e5c71c31920c427de435"}, @@ -2200,8 +2054,8 @@ pyrsistent = [ {file = "pyrsistent-0.16.0.tar.gz", hash = "sha256:28669905fe725965daa16184933676547c5bb40a5153055a8dee2a4bd7933ad3"}, ] pytest = [ - {file = "pytest-6.0.1-py3-none-any.whl", hash = "sha256:8b6007800c53fdacd5a5c192203f4e531eb2a1540ad9c752e052ec0f7143dbad"}, - {file = "pytest-6.0.1.tar.gz", hash = "sha256:85228d75db9f45e06e57ef9bf4429267f81ac7c0d742cc9ed63d09886a9fe6f4"}, + {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-2.10.1.tar.gz", hash = "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"}, @@ -2304,35 +2158,13 @@ pyzmq = [ {file = "pyzmq-19.0.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a7e7f930039ee0c4c26e4dfee015f20bd6919cd8b97c9cd7afbde2923a5167b6"}, {file = "pyzmq-19.0.2.tar.gz", hash = "sha256:296540a065c8c21b26d63e3cea2d1d57902373b16e4256afe46422691903a438"}, ] -regex = [ - {file = "regex-2020.7.14-cp27-cp27m-win32.whl", hash = "sha256:e46d13f38cfcbb79bfdb2964b0fe12561fe633caf964a77a5f8d4e45fe5d2ef7"}, - {file = "regex-2020.7.14-cp27-cp27m-win_amd64.whl", hash = "sha256:6961548bba529cac7c07af2fd4d527c5b91bb8fe18995fed6044ac22b3d14644"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:c50a724d136ec10d920661f1442e4a8b010a4fe5aebd65e0c2241ea41dbe93dc"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:8a51f2c6d1f884e98846a0a9021ff6861bdb98457879f412fdc2b42d14494067"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9c568495e35599625f7b999774e29e8d6b01a6fb684d77dee1f56d41b11b40cd"}, - {file = "regex-2020.7.14-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:51178c738d559a2d1071ce0b0f56e57eb315bcf8f7d4cf127674b533e3101f88"}, - {file = "regex-2020.7.14-cp36-cp36m-win32.whl", hash = "sha256:9eddaafb3c48e0900690c1727fba226c4804b8e6127ea409689c3bb492d06de4"}, - {file = "regex-2020.7.14-cp36-cp36m-win_amd64.whl", hash = "sha256:14a53646369157baa0499513f96091eb70382eb50b2c82393d17d7ec81b7b85f"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:1269fef3167bb52631ad4fa7dd27bf635d5a0790b8e6222065d42e91bede4162"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d0a5095d52b90ff38592bbdc2644f17c6d495762edf47d876049cfd2968fbccf"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:4c037fd14c5f4e308b8370b447b469ca10e69427966527edcab07f52d88388f7"}, - {file = "regex-2020.7.14-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:bc3d98f621898b4a9bc7fecc00513eec8f40b5b83913d74ccb445f037d58cd89"}, - {file = "regex-2020.7.14-cp37-cp37m-win32.whl", hash = "sha256:46bac5ca10fb748d6c55843a931855e2727a7a22584f302dd9bb1506e69f83f6"}, - {file = "regex-2020.7.14-cp37-cp37m-win_amd64.whl", hash = "sha256:0dc64ee3f33cd7899f79a8d788abfbec168410be356ed9bd30bbd3f0a23a7204"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_i686.whl", hash = "sha256:5ea81ea3dbd6767873c611687141ec7b06ed8bab43f68fad5b7be184a920dc99"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:bbb332d45b32df41200380fff14712cb6093b61bd142272a10b16778c418e98e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:c11d6033115dc4887c456565303f540c44197f4fc1a2bfb192224a301534888e"}, - {file = "regex-2020.7.14-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:75aaa27aa521a182824d89e5ab0a1d16ca207318a6b65042b046053cfc8ed07a"}, - {file = "regex-2020.7.14-cp38-cp38-win32.whl", hash = "sha256:d6cff2276e502b86a25fd10c2a96973fdb45c7a977dca2138d661417f3728341"}, - {file = "regex-2020.7.14-cp38-cp38-win_amd64.whl", hash = "sha256:7a2dd66d2d4df34fa82c9dc85657c5e019b87932019947faece7983f2089a840"}, - {file = "regex-2020.7.14.tar.gz", hash = "sha256:3a3af27a8d23143c49a3420efe5b3f8cf1a48c6fc8bc6856b03f638abc1833bb"}, -] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, ] rope = [ - {file = "rope-0.17.0.tar.gz", hash = "sha256:658ad6705f43dcf3d6df379da9486529cf30e02d9ea14c5682aa80eb33b649e1"}, + {file = "rope-1.1.1-py3-none-any.whl", hash = "sha256:216390b6342bde8ef1f27a6663554de0ea0b1fdad7c421deb3d5a3f6c5fc01e7"}, + {file = "rope-1.1.1.tar.gz", hash = "sha256:13048ff0245a0fa187f282697a55add46e4ccf083e7670f868bdf54ee9e47f2e"}, ] scikit-learn = [ {file = "scikit-learn-0.23.2.tar.gz", hash = "sha256:20766f515e6cd6f954554387dfae705d93c7b544ec0e6c6a5d8e006f6f7ef480"}, @@ -2438,9 +2270,13 @@ threadpoolctl = [ {file = "threadpoolctl-2.1.0-py3-none-any.whl", hash = "sha256:38b74ca20ff3bb42caca8b00055111d74159ee95c4370882bbff2b93d24da725"}, {file = "threadpoolctl-2.1.0.tar.gz", hash = "sha256:ddc57c96a38beb63db45d6c159b5ab07b6bced12c45a1f07b2b92f272aebfa6b"}, ] -toml = [ - {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, +tokenize-rt = [ + {file = "tokenize_rt-4.2.1-py2.py3-none-any.whl", hash = "sha256:08a27fa032a81cf45e8858d0ac706004fcd523e8463415ddf1442be38e204ea8"}, + {file = "tokenize_rt-4.2.1.tar.gz", hash = "sha256:0d4f69026fed520f8a1e0103aa36c406ef4661417f20ca643f913e33531b3b94"}, +] +tomli = [ + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] tornado = [ {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, @@ -2490,41 +2326,34 @@ traitlets = [ {file = "traitlets-4.3.3.tar.gz", hash = "sha256:d023ee369ddd2763310e4c3eae1ff649689440d4ae59d7485eb4cfbbe3e359f7"}, ] typed-ast = [ - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"}, - {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"}, - {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"}, - {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"}, - {file = "typed_ast-1.4.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:fcf135e17cc74dbfbc05894ebca928ffeb23d9790b3167a674921db19082401f"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"}, - {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"}, - {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"}, - {file = "typed_ast-1.4.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:f208eb7aff048f6bea9586e61af041ddf7f9ade7caed625742af423f6bae3298"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"}, - {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"}, - {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"}, - {file = "typed_ast-1.4.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:7e4c9d7658aaa1fc80018593abdf8598bf91325af6af5cce4ce7c73bc45ea53d"}, - {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"}, - {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"}, - {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:92c325624e304ebf0e025d1224b77dd4e6393f18aab8d829b5b7e04afe9b7a2c"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:d648b8e3bf2fe648745c8ffcee3db3ff903d0817a01a12dd6a6ea7a8f4889072"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:fac11badff8313e23717f3dada86a15389d0708275bddf766cca67a84ead3e91"}, - {file = "typed_ast-1.4.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:0d8110d78a5736e16e26213114a38ca35cb15b6515d535413b090bd50951556d"}, - {file = "typed_ast-1.4.1-cp39-cp39-win32.whl", hash = "sha256:b52ccf7cfe4ce2a1064b18594381bccf4179c2ecf7f513134ec2f993dd4ab395"}, - {file = "typed_ast-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:3742b32cf1c6ef124d57f95be609c473d7ec4c14d0090e5a5e05a15269fb4d0c"}, - {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] typing-extensions = [ - {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"}, - {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"}, - {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"}, + {file = "typing_extensions-4.3.0-py3-none-any.whl", hash = "sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02"}, + {file = "typing_extensions-4.3.0.tar.gz", hash = "sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6"}, ] urllib3 = [ {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"}, diff --git a/pybotics/geometry.py b/pybotics/geometry.py index eedf6ec3..2b833273 100644 --- a/pybotics/geometry.py +++ b/pybotics/geometry.py @@ -1,8 +1,9 @@ """Geometry functions and utilities.""" from enum import Enum -from typing import Sequence, Union +from typing import Union -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt from pybotics.errors import PyboticsError @@ -38,9 +39,9 @@ class OrientationConvention(Enum): def vector_2_matrix( - vector: Sequence[float], + vector: npt.NDArray[np.float64], convention: Union[OrientationConvention, str] = OrientationConvention.EULER_ZYX, -) -> np.ndarray: +) -> npt.NDArray[np.float64]: """ Calculate the pose from the position and euler angles. @@ -63,9 +64,9 @@ def vector_2_matrix( # iterate through rotation order # build rotation matrix transform_matrix = np.eye(4) - for axis, value in zip(convention, rotation_component): # type: ignore + for axis, value in zip(convention, rotation_component): current_rotation = globals()[f"rotation_matrix_{axis}"](value) - transform_matrix = np.dot(transform_matrix, current_rotation) + transform_matrix = np.dot(transform_matrix, current_rotation) # type: ignore # add translation component transform_matrix[:-1, -1] = translation_component @@ -73,24 +74,28 @@ def vector_2_matrix( return transform_matrix -def position_from_matrix(matrix: np.ndarray) -> np.ndarray: +def position_from_matrix(matrix: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: """Get the position values from a 4x4 transform matrix.""" - return matrix[:-1, -1] + position = matrix[:-1, -1] # type: npt.NDArray[np.float64] + return position def matrix_2_vector( - matrix: np.ndarray, + matrix: npt.NDArray[np.float64], convention: OrientationConvention = OrientationConvention.EULER_ZYX, -) -> np.ndarray: +) -> npt.NDArray[np.float64]: """Convert 4x4 matrix to a vector.""" # call function try: - return globals()[f"_matrix_2_{convention.name.lower()}"](matrix) + vector = globals()[f"_matrix_2_{convention.name.lower()}"]( + matrix + ) # type: npt.NDArray[np.float64] + return vector except KeyError: # pragma: no cover raise NotImplementedError -def _matrix_2_euler_zyx(matrix: np.ndarray) -> np.ndarray: +def _matrix_2_euler_zyx(matrix: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: """ Calculate the equivalent position and euler angles of the given pose. @@ -136,7 +141,7 @@ def wrap_2_pi(angle: float) -> float: return result -def rotation_matrix_x(angle: float) -> np.ndarray: +def rotation_matrix_x(angle: float) -> npt.NDArray[np.float64]: """Generate a basic 4x4 rotation matrix about the X axis.""" s = np.sin(angle) c = np.cos(angle) @@ -146,7 +151,7 @@ def rotation_matrix_x(angle: float) -> np.ndarray: return matrix -def rotation_matrix_y(angle: float) -> np.ndarray: +def rotation_matrix_y(angle: float) -> npt.NDArray[np.float64]: """Generate a basic 4x4 rotation matrix about the Y axis.""" s = np.sin(angle) c = np.cos(angle) @@ -156,7 +161,7 @@ def rotation_matrix_y(angle: float) -> np.ndarray: return matrix -def rotation_matrix_z(angle: float) -> np.ndarray: +def rotation_matrix_z(angle: float) -> npt.NDArray[np.float64]: """Generate a basic 4x4 rotation matrix about the Z axis.""" s = np.sin(angle) c = np.cos(angle) @@ -166,7 +171,7 @@ def rotation_matrix_z(angle: float) -> np.ndarray: return matrix -def translation_matrix(xyz: Sequence[float]) -> np.ndarray: +def translation_matrix(xyz: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: """Generate a basic 4x4 translation matrix.""" # validate if len(xyz) != 3: diff --git a/pybotics/json_encoder.py b/pybotics/json_encoder.py index d5a01c29..da92d8a5 100644 --- a/pybotics/json_encoder.py +++ b/pybotics/json_encoder.py @@ -2,7 +2,7 @@ import json from typing import Any -import numpy as np # type: ignore +import numpy as np class JSONEncoder(json.JSONEncoder): diff --git a/pybotics/kinematic_chain.py b/pybotics/kinematic_chain.py index 5c83c4fb..771ce8d5 100644 --- a/pybotics/kinematic_chain.py +++ b/pybotics/kinematic_chain.py @@ -4,7 +4,8 @@ from typing import Any, Optional, Sequence, Sized, Union import attr -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt from pybotics.errors import PyboticsError from pybotics.json_encoder import JSONEncoder @@ -30,7 +31,7 @@ def to_json(self) -> str: @property # type: ignore @abstractmethod - def matrix(self) -> np.ndarray: + def matrix(self) -> npt.NDArray[np.float64]: """ Convert chain to matrix of link parameters. @@ -41,7 +42,7 @@ def matrix(self) -> np.ndarray: @matrix.setter # type: ignore @abstractmethod - def matrix(self, value: np.ndarray) -> None: + def matrix(self, value: npt.NDArray[np.float64]) -> None: """ Set to matrix of link parameters. @@ -72,7 +73,9 @@ def num_parameters(self) -> int: raise NotImplementedError @abstractmethod - def transforms(self, q: Optional[Sequence[float]] = None) -> Sequence[np.ndarray]: + def transforms( + self, q: Optional[npt.NDArray[np.float64]] = None + ) -> Sequence[npt.NDArray[np.float64]]: """ Generate a sequence of spatial transforms. @@ -84,7 +87,7 @@ def transforms(self, q: Optional[Sequence[float]] = None) -> Sequence[np.ndarray @property @abstractmethod - def vector(self) -> np.ndarray: + def vector(self) -> npt.NDArray[np.float64]: """ Get the vector representation of the kinematic chain. @@ -93,12 +96,14 @@ def vector(self) -> np.ndarray: raise NotImplementedError @vector.setter - def vector(self, value: Sequence[float]) -> None: + def vector(self, value: npt.NDArray[np.float64]) -> None: """Set parameters of all links.""" raise NotImplementedError -def _validate_links(value: Union[Sequence[MDHLink], np.ndarray]) -> Sequence[MDHLink]: +def _validate_links( + value: Union[Sequence[MDHLink], npt.NDArray[np.float64]] +) -> Sequence[MDHLink]: if isinstance(value, np.ndarray): try: value = value.reshape((-1, MDHLink._size)) @@ -115,20 +120,20 @@ def _validate_links(value: Union[Sequence[MDHLink], np.ndarray]) -> Sequence[MDH class MDHKinematicChain(KinematicChain): """Kinematic Chain of MDH links.""" - _links = attr.ib(type=Union[Sequence[MDHLink], np.ndarray]) + _links = attr.ib(type=Sequence[MDHLink]) def __attrs_post_init__(self) -> None: """Post-attrs initialization.""" self._links = _validate_links(self._links) @classmethod - def from_parameters(cls: Any, parameters: Sequence[float]) -> Any: + def from_parameters(cls: Any, parameters: npt.NDArray[np.float64]) -> Any: """Construct Kinematic Chain from parameters.""" kc = cls(parameters) return kc @property - def matrix(self) -> np.ndarray: + def matrix(self) -> npt.NDArray[np.float64]: """ Convert chain to matrix of link parameters. @@ -138,7 +143,7 @@ def matrix(self) -> np.ndarray: return np.array([l.vector for l in self._links]) @matrix.setter - def matrix(self, value: np.ndarray) -> None: + def matrix(self, value: npt.NDArray[np.float64]) -> None: """ Set matrix of link parameters. @@ -155,7 +160,7 @@ def links(self) -> Sequence[MDHLink]: return x @links.setter - def links(self, value: Union[Sequence[MDHLink], np.ndarray]) -> None: + def links(self, value: Union[Sequence[MDHLink], npt.NDArray[np.float64]]) -> None: """Set links.""" self._links = _validate_links(value) @@ -169,20 +174,22 @@ def num_parameters(self) -> int: # noinspection PyProtectedMember return len(self) * MDHLink._size - def transforms(self, q: Optional[Sequence[float]] = None) -> Sequence[np.ndarray]: + def transforms( + self, q: Optional[npt.NDArray[np.float64]] = None + ) -> Sequence[npt.NDArray[np.float64]]: """Get sequence of 4x4 transforms.""" q = np.zeros(len(self)) if q is None else q transforms = [link.transform(p) for link, p in zip(self._links, q)] return transforms @property - def vector(self) -> np.ndarray: + def vector(self) -> npt.NDArray[np.float64]: """Get parameters of all links.""" return self.matrix.ravel() # noinspection PyMethodOverriding @vector.setter - def vector(self, value: Sequence[float]) -> None: + def vector(self, value: npt.NDArray[np.float64]) -> None: """Set parameters of all links.""" # noinspection PyProtectedMember value = np.array(value).reshape((-1, MDHLink._size)) diff --git a/pybotics/link.py b/pybotics/link.py index c3ec8af7..c137c5d0 100644 --- a/pybotics/link.py +++ b/pybotics/link.py @@ -1,10 +1,10 @@ """Link module.""" from abc import abstractmethod from collections.abc import Sized -from typing import Sequence, Union import attr -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt from pybotics.json_encoder import JSONEncoder @@ -23,7 +23,7 @@ def __len__(self) -> int: return self.size @abstractmethod - def displace(self, q: float) -> Union[Sequence[float], np.ndarray]: + def displace(self, q: float) -> npt.NDArray[np.float64]: """ Generate a vector of the new link state given a displacement. @@ -34,7 +34,7 @@ def displace(self, q: float) -> Union[Sequence[float], np.ndarray]: raise NotImplementedError @abstractmethod - def transform(self, q: float = 0) -> np.ndarray: + def transform(self, q: float = 0) -> npt.NDArray[np.float64]: """ Generate a 4x4 transform matrix given a displacement. @@ -46,7 +46,7 @@ def transform(self, q: float = 0) -> np.ndarray: @property @abstractmethod - def vector(self) -> np.ndarray: + def vector(self) -> npt.NDArray[np.float64]: """ Return the vector representation of the link. @@ -80,7 +80,7 @@ def size(self) -> int: """Get number of parameters.""" return self._size - def transform(self, q: float = 0) -> np.ndarray: + def transform(self, q: float = 0) -> npt.NDArray[np.float64]: """ Generate a 4x4 transform matrix with a displacement. @@ -113,7 +113,7 @@ def transform(self, q: float = 0) -> np.ndarray: return transform @property - def vector(self) -> np.ndarray: + def vector(self) -> npt.NDArray[np.float64]: """ Return the vector representation of the link. @@ -123,7 +123,7 @@ def vector(self) -> np.ndarray: # noinspection PyMethodOverriding @vector.setter - def vector(self, value: Sequence[float]) -> None: + def vector(self, value: npt.NDArray[np.float64]) -> None: """Set parameters.""" self.alpha = value[0] self.a = value[1] @@ -139,7 +139,7 @@ class RevoluteMDHLink(MDHLink): https://en.wikipedia.org/wiki/Denavit%E2%80%93Hartenberg_parameters """ - def displace(self, q: float = 0) -> Union[Sequence[float], np.ndarray]: + def displace(self, q: float = 0) -> npt.NDArray[np.float64]: """ Generate a vector of the new link state given a displacement. @@ -147,7 +147,7 @@ def displace(self, q: float = 0) -> Union[Sequence[float], np.ndarray]: :return vector of new displacement state """ - v = np.copy(self.vector) + v = self.vector.copy() v[2] += q return v @@ -160,7 +160,7 @@ class PrismaticMDHLink(MDHLink): https://en.wikipedia.org/wiki/Denavit%E2%80%93Hartenberg_parameters """ - def displace(self, q: float = 0) -> Union[Sequence[float], np.ndarray]: + def displace(self, q: float = 0) -> npt.NDArray[np.float64]: """ Generate a vector of the new link state given a displacement. @@ -168,6 +168,6 @@ def displace(self, q: float = 0) -> Union[Sequence[float], np.ndarray]: :return vector of new displacement state """ - v = np.copy(self.vector) + v = self.vector.copy() v[3] += q return v diff --git a/pybotics/optimization.py b/pybotics/optimization.py index e4d52d93..840c0737 100644 --- a/pybotics/optimization.py +++ b/pybotics/optimization.py @@ -1,22 +1,20 @@ -"""Optimization module. - -isort:skip_file -""" +"""Optimization module.""" from copy import deepcopy from itertools import repeat -from typing import Sequence, Union +from typing import MutableSequence, Sequence import attr -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt -from pybotics.robot import Robot from pybotics.errors import PyboticsError from pybotics.geometry import matrix_2_vector, position_from_matrix, vector_2_matrix +from pybotics.robot import Robot def _validate_transform_mask( - mask: Union[bool, Sequence[bool]], name: str, size: int -) -> Sequence[bool]: + mask: MutableSequence[bool], name: str, size: int +) -> MutableSequence[bool]: """Validate mask arguments.""" # validate input if isinstance(mask, bool): @@ -32,9 +30,9 @@ class OptimizationHandler: """Handler for optimization tasks.""" robot = attr.ib(type=Robot) - kinematic_chain_mask = attr.ib(False, type=Union[bool, Sequence[bool]]) - tool_mask = attr.ib(False, type=Union[bool, Sequence[bool]]) - world_mask = attr.ib(False, type=Union[bool, Sequence[bool]]) + kinematic_chain_mask = attr.ib(False, type=MutableSequence[bool]) + tool_mask = attr.ib(False, type=MutableSequence[bool]) + world_mask = attr.ib(False, type=MutableSequence[bool]) def __attrs_post_init__(self) -> None: """Post-init handler.""" @@ -51,14 +49,14 @@ def __attrs_post_init__(self) -> None: size=self.robot.kinematic_chain.num_parameters, ) - def apply_optimization_vector(self, vector: np.ndarray) -> None: + def apply_optimization_vector(self, vector: npt.NDArray[np.float64]) -> None: """Apply vector.""" # get number of parameters num_kc_parameters = np.sum(self.kinematic_chain_mask) num_tool_parameters = np.sum(self.tool_mask) # extract vector segments - segments = np.split( + segments = np.split( # type: ignore vector, [num_kc_parameters, num_kc_parameters + num_tool_parameters] ) kc_segment = segments[0] @@ -78,7 +76,7 @@ def apply_optimization_vector(self, vector: np.ndarray) -> None: world_vector[self.world_mask] = world_segment self.robot.world_frame = vector_2_matrix(world_vector) - def generate_optimization_vector(self) -> np.ndarray: + def generate_optimization_vector(self) -> npt.NDArray[np.float64]: """Generate vector.""" kc_vector = np.compress( self.kinematic_chain_mask, self.robot.kinematic_chain.vector @@ -91,28 +89,33 @@ def generate_optimization_vector(self) -> np.ndarray: def optimize_accuracy( - optimization_vector: np.ndarray, + optimization_vector: npt.NDArray[np.float64], handler: OptimizationHandler, - qs: Sequence[Sequence[float]], - positions: Sequence[Sequence[float]], -) -> np.ndarray: + qs: Sequence[npt.NDArray[np.float64]], + positions: Sequence[npt.NDArray[np.float64]], +) -> npt.NDArray[np.float64]: """Fitness function for accuracy optimization.""" handler.apply_optimization_vector(optimization_vector) errors = compute_absolute_errors(qs=qs, positions=positions, robot=handler.robot) return errors -def compute_absolute_error(q: np.ndarray, position: np.ndarray, robot: Robot) -> float: +def compute_absolute_error( + q: npt.NDArray[np.float64], position: npt.NDArray[np.float64], robot: Robot +) -> float: """Compute the absolute error of a given position.""" pose = robot.fk(q) actual_position = position_from_matrix(pose) - error = position - actual_position - return float(np.linalg.norm(error)) + error = position - actual_position # type: npt.NDArray[np.float64] + result = float(np.linalg.norm(error)) # type: ignore + return result def compute_absolute_errors( - qs: np.ndarray, positions: np.ndarray, robot: Robot -) -> np.ndarray: + qs: Sequence[npt.NDArray[np.float64]], + positions: Sequence[npt.NDArray[np.float64]], + robot: Robot, +) -> npt.NDArray[np.float64]: """ Compute the absolute errors of a given set of positions. @@ -120,11 +123,16 @@ def compute_absolute_errors( :param positions: Array of Cartesian positions, shape=(n-poses, 3) :param robot: Robot model """ - return list(map(compute_absolute_error, qs, positions, repeat(robot))) + return np.fromiter( # type: ignore + map(compute_absolute_error, qs, positions, repeat(robot)), dtype=np.float64 + ) def compute_relative_error( - q_a: np.ndarray, q_b: np.ndarray, distance: float, robot: Robot + q_a: npt.NDArray[np.float64], + q_b: npt.NDArray[np.float64], + distance: float, + robot: Robot, ) -> float: """Compute the relative error of a given position combination.""" pose_a = robot.fk(q_a) @@ -133,16 +141,22 @@ def compute_relative_error( actual_position_a = position_from_matrix(pose_a) actual_position_b = position_from_matrix(pose_b) - actual_distance = actual_position_a - actual_position_b - actual_distance = np.linalg.norm(actual_distance) + actual_distance = actual_position_a - actual_position_b # type: float + actual_distance = np.linalg.norm(actual_distance) # type: ignore - error = float(np.linalg.norm(distance - actual_distance)) + error = float(np.linalg.norm(distance - actual_distance)) # type: ignore return error def compute_relative_errors( - qs_a: np.ndarray, qs_b: np.ndarray, distances: np.ndarray, robot: Robot -) -> np.array: + qs_a: npt.NDArray[np.float64], + qs_b: npt.NDArray[np.float64], + distances: npt.NDArray[np.float64], + robot: Robot, +) -> npt.NDArray[np.float64]: """Compute the relative errors of a given set of position combinations.""" - return list(map(compute_relative_error, qs_a, qs_b, distances, repeat(robot))) + return np.fromiter( # type: ignore + map(compute_relative_error, qs_a, qs_b, distances, repeat(robot)), + dtype=np.float64, + ) diff --git a/pybotics/predefined_models.py b/pybotics/predefined_models.py index 6bd906b8..7cbc7e32 100644 --- a/pybotics/predefined_models.py +++ b/pybotics/predefined_models.py @@ -3,10 +3,11 @@ These models correspond to the Modified Denavit–Hartenberg parameters: https://en.wikipedia.org/wiki/Denavit%E2%80%93Hartenberg_parameters """ -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt -def kuka_lbr_iiwa_7() -> np.ndarray: # pragma: no cover +def kuka_lbr_iiwa_7() -> npt.NDArray[np.float64]: # pragma: no cover """Get KUKA LBR iiwa 7 MDH model.""" return np.array( [ @@ -21,7 +22,7 @@ def kuka_lbr_iiwa_7() -> np.ndarray: # pragma: no cover ) -def mecademic_meca500() -> np.ndarray: # pragma: no cover +def mecademic_meca500() -> npt.NDArray[np.float64]: # pragma: no cover """Get Meca500 MDH model.""" return np.array( [ @@ -35,7 +36,7 @@ def mecademic_meca500() -> np.ndarray: # pragma: no cover ) -def puma560() -> np.ndarray: # pragma: no cover +def puma560() -> npt.NDArray[np.float64]: # pragma: no cover """Get PUMA560 MDH model.""" return np.array( [ @@ -49,7 +50,7 @@ def puma560() -> np.ndarray: # pragma: no cover ) -def ur10() -> np.ndarray: # pragma: no cover +def ur10() -> npt.NDArray[np.float64]: # pragma: no cover """Get UR10 MDH model.""" return np.array( [ @@ -63,7 +64,7 @@ def ur10() -> np.ndarray: # pragma: no cover ) -def abb_irb120() -> np.ndarray: # pragma: no cover +def abb_irb120() -> npt.NDArray[np.float64]: # pragma: no cover """Get ABB irb120 MDH model.""" return np.array( [ diff --git a/pybotics/robot.py b/pybotics/robot.py index 53069678..e9e2ea6b 100644 --- a/pybotics/robot.py +++ b/pybotics/robot.py @@ -1,8 +1,12 @@ """Robot module.""" -from typing import Any, Optional, Sequence, Sized, Union +from __future__ import annotations + +import typing +from typing import Any, Optional, Sized import attr -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt import scipy.optimize # type: ignore from pybotics.errors import PyboticsError @@ -11,11 +15,11 @@ from pybotics.tool import Tool -def _ndof_zeros_factory(robot: Any) -> np.ndarray: +def _ndof_zeros_factory(robot: Any) -> npt.NDArray[np.float64]: return np.zeros(len(robot.kinematic_chain)) -def _joint_limits_factory(robot: Any) -> np.ndarray: +def _joint_limits_factory(robot: Any) -> npt.NDArray[np.float64]: return np.repeat((-np.pi, np.pi), len(robot.kinematic_chain)).reshape((2, -1)) @@ -25,22 +29,22 @@ class Robot(Sized): kinematic_chain = attr.ib(type=KinematicChain) tool = attr.ib(factory=lambda: Tool(), type=Tool) - world_frame = attr.ib(factory=lambda: np.eye(4), type=np.ndarray) # type: ignore + world_frame = attr.ib(factory=lambda: np.eye(4), type=npt.NDArray[np.float64]) random_state = attr.ib( - factory=lambda: np.random.RandomState(), # type: ignore + factory=lambda: np.random.RandomState(), type=np.random.RandomState, ) home_position = attr.ib( default=attr.Factory(factory=_ndof_zeros_factory, takes_self=True), - type=np.ndarray, + type=npt.NDArray[np.float64], ) _joints = attr.ib( default=attr.Factory(factory=_ndof_zeros_factory, takes_self=True), - type=np.ndarray, + type=npt.NDArray[np.float64], ) _joint_limits = attr.ib( default=attr.Factory(factory=_joint_limits_factory, takes_self=True), - type=np.ndarray, + type=npt.NDArray[np.float64], ) def __len__(self) -> int: @@ -56,7 +60,9 @@ def to_json(self) -> str: encoder = JSONEncoder(sort_keys=True) return encoder.encode(self) - def fk(self, q: Optional[Sequence[float]] = None) -> np.ndarray: + def fk( + self, q: Optional[npt.NDArray[np.float64]] = None + ) -> npt.NDArray[np.float64]: """ Compute the forward kinematics of a given position. @@ -77,24 +83,30 @@ def fk(self, q: Optional[Sequence[float]] = None) -> np.ndarray: # matrix multiply through transforms pose = np.eye(4, dtype=float) for t in transforms: - pose = np.dot(pose, t) + pose = np.dot(pose, t) # type: ignore return pose def ik( - self, pose: np.ndarray, q: Optional[Sequence[float]] = None - ) -> Optional[np.ndarray]: + self, pose: npt.NDArray[np.float64], q: Optional[npt.NDArray[np.float64]] = None + ) -> Optional[npt.NDArray[np.float64]]: """Solve the inverse kinematics.""" + # set initial value x0 = self.joints if q is None else q - result = scipy.optimize.least_squares( + + # solve optimization + optimization_result = scipy.optimize.least_squares( fun=_ik_cost_function, x0=x0, bounds=self.joint_limits, args=(pose, self) ) # type: scipy.optimize.OptimizeResult - if result.success: # pragma: no cover - actual_pose = self.fk(result.x) + # set return value + result = None # type: Optional[npt.NDArray[np.float64]] + if optimization_result.success: # pragma: no cover + actual_pose = self.fk(optimization_result.x) if np.allclose(actual_pose, pose, atol=1e-3): - return result.x - return None + result = optimization_result.x + + return result @property def ndof(self) -> int: @@ -106,7 +118,7 @@ def ndof(self) -> int: return len(self) @property - def joints(self) -> Union[Sequence[float], np.ndarray]: + def joints(self) -> npt.NDArray[np.float64]: """ Get the robot configuration (e.g., joint positions for serial robot). @@ -115,14 +127,14 @@ def joints(self) -> Union[Sequence[float], np.ndarray]: return self._joints @joints.setter - def joints(self, value: np.ndarray) -> None: + def joints(self, value: npt.NDArray[np.float64]) -> None: """Set joints.""" if np.any(value < self.joint_limits[0]) or np.any(value > self.joint_limits[1]): raise PyboticsError("Joint limits exceeded.") self._joints = value @property - def joint_limits(self) -> np.ndarray: + def joint_limits(self) -> npt.NDArray[np.float64]: """ Limits of the robot position (e.g., joint limits). @@ -131,13 +143,15 @@ def joint_limits(self) -> np.ndarray: return self._joint_limits @joint_limits.setter - def joint_limits(self, value: np.ndarray) -> None: + def joint_limits(self, value: npt.NDArray[np.float64]) -> None: """Set joint limits.""" if value.shape[0] != 2 or value.shape[1] != len(self): raise PyboticsError(f"position_limits must have shape=(2,{len(self)})") self._joint_limits = value - def jacobian_world(self, q: Optional[Sequence[float]] = None) -> np.ndarray: + def jacobian_world( + self, q: Optional[npt.NDArray[np.float64]] = None + ) -> npt.NDArray[np.float64]: """Calculate the Jacobian wrt the world frame.""" q = self.joints if q is None else q j_fl = self.jacobian_flange(q) @@ -146,11 +160,13 @@ def jacobian_world(self, q: Optional[Sequence[float]] = None) -> np.ndarray: j_tr = np.zeros((6, 6), dtype=float) j_tr[:3, :3] = rotation j_tr[3:, 3:] = rotation - j_w = np.dot(j_tr, j_fl) + j_w = np.dot(j_tr, j_fl) # type: ignore - return j_w + return typing.cast(npt.NDArray[np.float64], j_w) - def jacobian_flange(self, q: Optional[Sequence[float]] = None) -> np.ndarray: + def jacobian_flange( + self, q: Optional[npt.NDArray[np.float64]] = None + ) -> npt.NDArray[np.float64]: """Calculate the Jacobian wrt the flange frame.""" q = self.joints if q is None else q @@ -176,13 +192,17 @@ def jacobian_flange(self, q: Optional[Sequence[float]] = None) -> np.ndarray: current_link = self.kinematic_chain.links[i] p = q[i] current_link_transform = current_link.transform(p) - current_transform = np.dot(current_link_transform, current_transform) + current_transform = np.dot( # type: ignore + current_link_transform, current_transform + ) return jacobian_flange def compute_joint_torques( - self, wrench: Sequence[float], q: Optional[Sequence[float]] = None - ) -> np.ndarray: + self, + wrench: npt.NDArray[np.float64], + q: Optional[npt.NDArray[np.float64]] = None, + ) -> npt.NDArray[np.float64]: """ Calculate the joint torques due to external flange force. @@ -215,11 +235,11 @@ def compute_joint_torques( # calculate force applied to current link rotation = transform[:3, :3] - force = np.dot(rotation, force) + force = np.dot(rotation, force) # type: ignore # calculate moment applied to current link q = transform[:3, -1] - moment = np.dot(rotation, moment) + np.cross(q, force) + moment = np.dot(rotation, moment) + np.cross(q, force) # type: ignore # append z-component as joint torque joint_torques.append(moment[-1]) @@ -227,31 +247,41 @@ def compute_joint_torques( # reverse torques into correct order return np.array(list(reversed(joint_torques)), dtype=float) - def clamp_joints(self, q: Sequence[float]) -> Optional[np.ndarray]: + def clamp_joints(self, q: npt.NDArray[np.float64]) -> npt.NDArray[np.float64]: """Limit joints to joint limits.""" - return np.clip(q, self.joint_limits[0], self.joint_limits[1]) - - def random_joints(self, in_place: bool = False) -> Optional[np.ndarray]: + result = np.clip( + q, self.joint_limits[0], self.joint_limits[1] + ) # type: npt.NDArray[np.float64] + return result + + def random_joints( + self, in_place: bool = False + ) -> Optional[npt.NDArray[np.float64]]: """Generate random joints within limits.""" q = self.random_state.uniform( low=self.joint_limits[0], high=self.joint_limits[1] ) + result = None if in_place: self.joints = q - return None else: - return q + result = q + + return result @classmethod - def from_parameters(cls, parameters: Sequence[float]) -> Sized: + def from_parameters(cls, parameters: npt.NDArray[np.float64]) -> Robot: """Construct Robot from Kinematic Chain parameters.""" # FIXME: assumes MDH revolute robot kc = MDHKinematicChain.from_parameters(parameters) return cls(kinematic_chain=kc) -def _ik_cost_function(q: np.ndarray, pose: np.ndarray, robot: Robot) -> np.ndarray: +def _ik_cost_function( + q: npt.NDArray[np.float64], pose: npt.NDArray[np.float64], robot: Robot +) -> npt.NDArray[np.float64]: actual_pose = robot.fk(q) diff = np.abs(actual_pose - pose) - return diff.ravel() + result = diff.ravel() # type: npt.NDArray[np.float64] + return result diff --git a/pybotics/tool.py b/pybotics/tool.py index 8cfc9c52..92cf777d 100644 --- a/pybotics/tool.py +++ b/pybotics/tool.py @@ -2,10 +2,11 @@ isort:skip_file """ -from typing import Sequence, Union import attr -import numpy as np # type: ignore +import numpy as np +import numpy.typing as npt + from pybotics.geometry import matrix_2_vector, position_from_matrix, vector_2_matrix @@ -14,12 +15,12 @@ class Tool: """Tool class.""" - matrix = attr.ib(factory=lambda: np.eye(4), type=np.ndarray) # type: ignore + matrix = attr.ib(factory=lambda: np.eye(4), type=npt.NDArray[np.float64]) mass = attr.ib(0, type=float) - cg = attr.ib(factory=lambda: np.zeros(3), type=np.ndarray) # type: ignore + cg = attr.ib(factory=lambda: np.zeros(3), type=npt.NDArray[np.float64]) @property - def position(self) -> Union[Sequence[float], np.ndarray]: + def position(self) -> npt.NDArray[np.float64]: """ Get the position XYZ of the frame. @@ -28,11 +29,11 @@ def position(self) -> Union[Sequence[float], np.ndarray]: return position_from_matrix(self.matrix) @position.setter - def position(self, value: Sequence[float]) -> None: + def position(self, value: npt.NDArray[np.float64]) -> None: self.matrix[:-1, -1] = value @property - def vector(self) -> np.ndarray: + def vector(self) -> npt.NDArray[np.float64]: """ Return the vector representation of the frame as EULER ZYX. @@ -41,5 +42,5 @@ def vector(self) -> np.ndarray: return matrix_2_vector(self.matrix) @vector.setter - def vector(self, value: Sequence[float]) -> None: + def vector(self, value: npt.NDArray[np.float64]) -> None: self.matrix = vector_2_matrix(value) diff --git a/pyproject.toml b/pyproject.toml index 5eb59d84..8836288d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,9 +56,10 @@ python = "^3.7" attrs = ">=19.0" numpy = "^1.20" scipy = "^1" +typing-extensions = { version = "^4", python = "~3.7" } [tool.poetry.dev-dependencies] -black = ">=22.1" +black = {extras = ["jupyter"], version = "^22.6.0"} coverage = ">=6" flake8 = ">=4" flake8-bugbear = "*" @@ -88,7 +89,6 @@ line-length = 88 target_version = ['py37'] include = '\.pyi?$' exclude = ''' - ( /( \.eggs # exclude a few common directories in the @@ -107,6 +107,9 @@ exclude = ''' ) ''' +[tool.isort] +profile = "black" + [build-system] requires = ["poetry_core>=1.0.0"] build-backend = "poetry.core.masonry.api" diff --git a/tests/conftest.py b/tests/conftest.py deleted file mode 100644 index dee7382c..00000000 --- a/tests/conftest.py +++ /dev/null @@ -1,38 +0,0 @@ -"""Pytest config.""" -from pathlib import Path - -import numpy as np -from pytest import fixture - -from pybotics.kinematic_chain import MDHKinematicChain -from pybotics.robot import Robot - - -@fixture() -def planar_robot(): - """Generate planar robot.""" - return Robot( - MDHKinematicChain(np.array([[0, 0, 0, 0], [0, 10, 0, 0], [0, 20, 0, 0]])) - ) - - -@fixture() -def resources_path(): - """Get resources path.""" - return (Path(__file__).parent / "resources").resolve() - - -@fixture() -def vector_transforms(resources_path: Path): - """Get resource data.""" - data = np.genfromtxt( - fname=resources_path / "vector-transforms.csv", delimiter=",", dtype=str - ) - return [ - { - "vector": d[:6].astype(float), - "transform": d[6:-1].astype(float), - "order": d[-1], - } - for d in data - ] diff --git a/tests/resources/rotx-transforms.csv b/tests/resources/rotx-transforms.csv deleted file mode 100644 index d6d561d1..00000000 --- a/tests/resources/rotx-transforms.csv +++ /dev/null @@ -1,4 +0,0 @@ -# angle[0], transform[1:] -0.17453292519943295,1.0,0.0,0.0,0.0,0.0,0.9848077,-0.1736482,0.0,0.0,0.1736482,0.9848077,0.0,0.0,0.0,0.0,1.0 - - diff --git a/tests/resources/roty-transforms.csv b/tests/resources/roty-transforms.csv deleted file mode 100644 index a68debe8..00000000 --- a/tests/resources/roty-transforms.csv +++ /dev/null @@ -1,4 +0,0 @@ -# angle[0], transform[1:] -0.17453292519943295,0.9848077,0.0,0.1736482,0.0,0.0,1.0,0.0,0.0,-0.1736482,0.0,0.9848077,0.0,0.0,0.0,0.0,1.0 - - diff --git a/tests/resources/rotz-transforms.csv b/tests/resources/rotz-transforms.csv deleted file mode 100644 index b8ca5560..00000000 --- a/tests/resources/rotz-transforms.csv +++ /dev/null @@ -1,4 +0,0 @@ -# angle[0], transform[1:] -0.17453292519943295,0.9848077,-0.1736482,0.0,0.0,0.1736482,0.9848077,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,1.0 - - diff --git a/tests/test_errors.py b/tests/test_errors.py index 4f4c41d4..beb2c4c2 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -5,7 +5,7 @@ from pybotics.errors import PyboticsError -def test_errors(): +def test_errors() -> None: """ Test error. diff --git a/tests/test_geometry.py b/tests/test_geometry.py index 30c74762..d1f021dd 100644 --- a/tests/test_geometry.py +++ b/tests/test_geometry.py @@ -1,21 +1,48 @@ """Test geometry.""" +import typing from collections import Counter from pathlib import Path -from typing import Sequence +from typing import Dict, List, Sequence, Union import hypothesis.strategies as st import numpy as np -from hypothesis import given, settings +import numpy.typing as npt +from hypothesis import given from hypothesis.extra.numpy import arrays -from pytest import raises +from pytest import fixture, raises +from scipy.spatial.transform import Rotation # type: ignore import pybotics.geometry from pybotics.errors import PyboticsError from pybotics.geometry import OrientationConvention, matrix_2_vector +MIN_FLOAT = -1e9 +MAX_FLOAT = 1e9 + + +@fixture() +def vector_transforms() -> List[Dict[str, Union[npt.NDArray[np.float64], str]]]: + """Get resource data.""" + + # load test data + data_path = ( + Path(__file__).parent / "resources" + ).resolve() / "vector-transforms.csv" + data = np.genfromtxt(fname=data_path, delimiter=",", dtype=str) # type: ignore + + result = [ + { + "vector": d[:6].astype(float), + "transform": d[6:-1].astype(float), + "order": d[-1], + } + for d in data + ] + return result + @given(st.floats(allow_nan=False, allow_infinity=False)) -def test_wrap_2_pi(angle): +def test_wrap_2_pi(angle: float) -> None: """ Test angle wrapping. @@ -35,63 +62,126 @@ def test_wrap_2_pi(angle): actual_angles = np.array(list(map(pybotics.geometry.wrap_2_pi, test_angles))) assert len(test_angles) == len(expected_angles) assert len(actual_angles) == len(expected_angles) - np.testing.assert_allclose(actual_angles, expected_angles) + np.testing.assert_allclose(actual_angles, expected_angles) # type: ignore # test single elements for i, _ in enumerate(expected_angles): actual_angle = pybotics.geometry.wrap_2_pi(test_angles[i]) - np.testing.assert_allclose([actual_angle], expected_angles[i]) + np.testing.assert_allclose([actual_angle], expected_angles[i]) # type: ignore -@given(angle=st.floats(allow_nan=False, allow_infinity=False)) -@settings(deadline=None) -def test_rotation_matrix_xyz(angle, resources_path: Path): - """Test.""" - # define functions to test - rotation_functions = { - "x": pybotics.geometry.rotation_matrix_x, - "y": pybotics.geometry.rotation_matrix_y, - "z": pybotics.geometry.rotation_matrix_z, - } - - # iterate through rotation axes - for i, axis in enumerate("xyz"): - # get resource file - path = resources_path / f"rot{axis}-transforms.csv" - data = np.loadtxt(str(path.resolve()), delimiter=",") - if data.ndim == 1: - data = np.expand_dims(data, axis=0) - - # test resource transforms - for d in data: - actual_matrix = rotation_functions[axis](d[0]) - np.testing.assert_allclose( - actual=actual_matrix, desired=d[1:].reshape((4, 4)), atol=1e-6 - ) +@given( + angle=st.floats( + allow_nan=False, allow_infinity=False, min_value=MIN_FLOAT, max_value=MAX_FLOAT + ) +) +def test_rotation_matrix_x(angle: float) -> None: + # set axis-specific parameters + axis_idx = 0 + axis_vector = np.zeros(3) + axis_vector[axis_idx] = 1 - # test hypothesis transforms - actual_matrix = rotation_functions[axis](angle) + # test randomized hypothesis transforms + actual_matrix = pybotics.geometry.rotation_matrix_x(angle) - # check orthogonality - for row in actual_matrix: - # noinspection PyTypeChecker - np.testing.assert_allclose(np.linalg.norm(row), 1) + # check orthogonality + for row in actual_matrix: + np.testing.assert_allclose(np.linalg.norm(row), 1) # type: ignore - for column in actual_matrix.T: - # noinspection PyTypeChecker - np.testing.assert_allclose(np.linalg.norm(column), 1) + for column in actual_matrix.T: + np.testing.assert_allclose(np.linalg.norm(column), 1) # type: ignore - # check no translation - # noinspection PyTypeChecker - np.testing.assert_allclose(actual_matrix[:-1, -1], 0) + # check no translation + np.testing.assert_allclose(actual_matrix[:-1, -1], 0) # type: ignore - # check homogeneous matrix - # noinspection PyTypeChecker - np.testing.assert_allclose(actual_matrix[-1, :-1], 0) + # check homogeneous matrix + np.testing.assert_allclose(actual_matrix[-1, :-1], 0) # type: ignore - # check unit vector location - # noinspection PyTypeChecker - np.testing.assert_allclose(actual_matrix[i, i], 1) + # check unit vector location + np.testing.assert_allclose(actual_matrix[axis_idx, axis_idx], 1) # type: ignore + + # check 3x3 rotation component + actual_rotation = actual_matrix[:3, :3] + expected_rotation = Rotation.from_rotvec(angle * axis_vector).as_matrix() + np.testing.assert_allclose( # type: ignore + actual=actual_rotation, desired=expected_rotation, rtol=1e-05, atol=1e-08 + ) + + +@given( + angle=st.floats( + allow_nan=False, allow_infinity=False, min_value=MIN_FLOAT, max_value=MAX_FLOAT + ) +) +def test_rotation_matrix_y(angle: float) -> None: + # set axis-specific parameters + axis_idx = 1 + axis_vector = np.zeros(3) + axis_vector[axis_idx] = 1 + + # test randomized hypothesis transforms + actual_matrix = pybotics.geometry.rotation_matrix_y(angle) + + # check orthogonality + for row in actual_matrix: + np.testing.assert_allclose(np.linalg.norm(row), 1) # type: ignore + + for column in actual_matrix.T: + np.testing.assert_allclose(np.linalg.norm(column), 1) # type: ignore + + # check no translation + np.testing.assert_allclose(actual_matrix[:-1, -1], 0) # type: ignore + + # check homogeneous matrix + np.testing.assert_allclose(actual_matrix[-1, :-1], 0) # type: ignore + + # check unit vector location + np.testing.assert_allclose(actual_matrix[axis_idx, axis_idx], 1) # type: ignore + + # check 3x3 rotation component + actual_rotation = actual_matrix[:3, :3] + expected_rotation = Rotation.from_rotvec(angle * axis_vector).as_matrix() + np.testing.assert_allclose( # type: ignore + actual=actual_rotation, desired=expected_rotation, rtol=1e-05, atol=1e-08 + ) + + +@given( + angle=st.floats( + allow_nan=False, allow_infinity=False, min_value=MIN_FLOAT, max_value=MAX_FLOAT + ) +) +def test_rotation_matrix_z(angle: float) -> None: + # set axis-specific parameters + axis_idx = 2 + axis_vector = np.zeros(3) + axis_vector[axis_idx] = 1 + + # test randomized hypothesis transforms + actual_matrix = pybotics.geometry.rotation_matrix_z(angle) + + # check orthogonality + for row in actual_matrix: + np.testing.assert_allclose(np.linalg.norm(row), 1) # type: ignore + + for column in actual_matrix.T: + np.testing.assert_allclose(np.linalg.norm(column), 1) # type: ignore + + # check no translation + np.testing.assert_allclose(actual_matrix[:-1, -1], 0) # type: ignore + + # check homogeneous matrix + np.testing.assert_allclose(actual_matrix[-1, :-1], 0) # type: ignore + + # check unit vector location + np.testing.assert_allclose(actual_matrix[axis_idx, axis_idx], 1) # type: ignore + + # check 3x3 rotation component + actual_rotation = actual_matrix[:3, :3] + expected_rotation = Rotation.from_rotvec(angle * axis_vector).as_matrix() + np.testing.assert_allclose( # type: ignore + actual=actual_rotation, desired=expected_rotation, rtol=1e-05, atol=1e-08 + ) @given( @@ -101,39 +191,43 @@ def test_rotation_matrix_xyz(angle, resources_path: Path): elements=st.floats(allow_nan=False, allow_infinity=False), ) ) -def test_translation_matrix(xyz): +def test_translation_matrix(xyz: npt.NDArray[np.float64]) -> None: """Test.""" matrix = pybotics.geometry.translation_matrix(xyz) # check orthogonality for row in matrix[:-1, :-1]: # noinspection PyTypeChecker - np.testing.assert_allclose(np.linalg.norm(row), 1) + np.testing.assert_allclose(np.linalg.norm(row), 1) # type: ignore for column in matrix[:, :-1].T: # noinspection PyTypeChecker - np.testing.assert_allclose(np.linalg.norm(column), 1) + np.testing.assert_allclose(np.linalg.norm(column), 1) # type: ignore # check translation # noinspection PyTypeChecker - np.testing.assert_allclose(matrix[:-1, -1], xyz) + np.testing.assert_allclose(matrix[:-1, -1], xyz) # type: ignore # check homogeneous matrix # noinspection PyTypeChecker - np.testing.assert_allclose(matrix[-1, :-1], 0) + np.testing.assert_allclose(matrix[-1, :-1], 0) # type: ignore # test exception with raises(PyboticsError): pybotics.geometry.translation_matrix(np.zeros(10)) -def test_vector_2_matrix(vector_transforms: Sequence[dict]): +def test_vector_2_matrix( + vector_transforms: Sequence[typing.Dict[str, npt.NDArray[np.float64]]] +) -> None: """Test.""" # test regular usage for d in vector_transforms: for c in [d["order"], OrientationConvention(d["order"])]: - actual = pybotics.geometry.vector_2_matrix(d["vector"], convention=c) - np.testing.assert_allclose( + actual = pybotics.geometry.vector_2_matrix( + d["vector"], convention=c # type: ignore + ) + np.testing.assert_allclose( # type: ignore actual=actual, desired=d["transform"].reshape((4, 4)), atol=1e-6 ) @@ -142,7 +236,9 @@ def test_vector_2_matrix(vector_transforms: Sequence[dict]): pybotics.geometry.vector_2_matrix(d["vector"], convention="foobar") -def test_matrix_2_vector(vector_transforms: Sequence[dict]): +def test_matrix_2_vector( + vector_transforms: Sequence[typing.Dict[str, npt.NDArray[np.float64]]] +) -> None: """Test.""" for d in vector_transforms: for c in [ @@ -156,12 +252,12 @@ def test_matrix_2_vector(vector_transforms: Sequence[dict]): # TODO: implement other conversions # don't fail for NotImplementedError continue - np.testing.assert_allclose( + np.testing.assert_allclose( # type: ignore actual=actual_vector, desired=d["vector"], atol=1e-6 ) -def test_orientation(): +def test_orientation() -> None: """Test.""" # ensure order and name match for e in list(OrientationConvention.__members__.values()): @@ -175,6 +271,6 @@ def test_orientation(): # ensure only x,y,z are used good_letters = set("xyz") - values = set([e.value for e in OrientationConvention.__members__.values()]) + values = list(set([e.value for e in OrientationConvention.__members__.values()])) leftover_values = [x for x in values if set(x).difference(good_letters)] assert not leftover_values diff --git a/tests/test_kinematic_chain.py b/tests/test_kinematic_chain.py index 1290758b..beaaaa68 100644 --- a/tests/test_kinematic_chain.py +++ b/tests/test_kinematic_chain.py @@ -7,52 +7,52 @@ from pybotics.link import MDHLink, RevoluteMDHLink -def test_init(): +def test_init() -> None: """Test.""" # test error with raises(PyboticsError): - MDHKinematicChain(np.eye(5)) + MDHKinematicChain(np.eye(5)) # type: ignore # test sequence of links MDHKinematicChain([RevoluteMDHLink()]) -def test_num_parameters(): +def test_num_parameters() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) assert kc.num_parameters == MDHLink._size -def test_vector(): +def test_vector() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) - np.testing.assert_allclose(kc.vector, link.vector) + np.testing.assert_allclose(kc.vector, link.vector) # type: ignore -def test_repr(): +def test_repr() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) repr(kc) -def test_to_json(): +def test_to_json() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) kc.to_json() -def test_links_setter(): +def test_links_setter() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) - kc.links = link + kc.links = [link] -def test_ndof(): +def test_ndof() -> None: """Test.""" link = RevoluteMDHLink() kc = MDHKinematicChain([link]) diff --git a/tests/test_link.py b/tests/test_link.py index 17f9acc1..5cf11976 100644 --- a/tests/test_link.py +++ b/tests/test_link.py @@ -4,24 +4,24 @@ from pybotics.link import PrismaticMDHLink, RevoluteMDHLink -def test_len(): +def test_len() -> None: """Test.""" assert len(RevoluteMDHLink()) == 4 -def test_displace(): +def test_displace() -> None: """Test.""" link = PrismaticMDHLink() - np.testing.assert_allclose(link.displace(), link.vector) + np.testing.assert_allclose(link.displace(), link.vector) # type: ignore -def test_repr(): +def test_repr() -> None: """Test.""" link = RevoluteMDHLink() repr(link) -def test_json(): +def test_json() -> None: """Test.""" link = RevoluteMDHLink() link.to_json() diff --git a/tests/test_optimization.py b/tests/test_optimization.py index 09db379a..1550b9a6 100644 --- a/tests/test_optimization.py +++ b/tests/test_optimization.py @@ -4,7 +4,8 @@ """ import hypothesis.strategies as st import numpy as np -import scipy.optimize +import numpy.typing as npt +import scipy.optimize # type: ignore from hypothesis import given from hypothesis.extra.numpy import arrays from pytest import raises @@ -29,21 +30,24 @@ elements=st.floats(allow_nan=False, allow_infinity=False), ) ) -def test_compute_absolute_errors(q: np.ndarray): +def test_compute_absolute_errors(q: npt.NDArray[np.float64]) -> None: """Test.""" robot = Robot.from_parameters(ur10()) pose = robot.fk(q) - p = pose[:-1, -1] + position_vector = pose[:-1, -1] - # test 1D input - actual_error = compute_absolute_error(q=q, position=p, robot=robot) - np.testing.assert_allclose(actual_error, 0) + # test 1D input; i.e., only one pose + actual_error = compute_absolute_error(q=q, position=position_vector, robot=robot) + np.testing.assert_allclose(actual_error, 0) # type: ignore - # test 2D input - actual_error = compute_absolute_errors( - qs=np.tile(q, (10, 1)), positions=np.tile(p, (10, 1)), robot=robot + # test 2D input; i.e., many poses + num_repeats = 10 + actual_error = compute_absolute_errors( # type: ignore + qs=np.tile(q, (num_repeats, 1)), # type: ignore + positions=np.tile(position_vector, (num_repeats, 1)), # type: ignore + robot=robot, ) - np.testing.assert_allclose(actual_error, 0) + np.testing.assert_allclose(actual_error, 0) # type: ignore @given( @@ -58,39 +62,44 @@ def test_compute_absolute_errors(q: np.ndarray): elements=st.floats(allow_nan=False, allow_infinity=False), ), ) -def test_compute_relative_errors(q_a: np.ndarray, q_b: np.ndarray): +def test_compute_relative_errors( + q_a: npt.NDArray[np.float64], q_b: npt.NDArray[np.float64] +) -> None: """Test.""" robot = Robot.from_parameters(ur10()) p_a = robot.fk(q_a)[:-1, -1] p_b = robot.fk(q_b)[:-1, -1] - distance = np.linalg.norm(p_a - p_b) + distance = np.linalg.norm(p_a - p_b) # type: ignore - # test 1D input + # test 1D input; i.e., only one pose actual_error = compute_relative_error( q_a=q_a, q_b=q_b, distance=distance, robot=robot ) - np.testing.assert_allclose(actual_error, 0) - - # test 2D input - actual_error = compute_relative_errors( - qs_a=np.tile(q_a, (10, 1)), - qs_b=np.tile(q_b, (10, 1)), - distances=np.tile(distance, (10, 1)), + np.testing.assert_allclose(actual_error, 0) # type: ignore + + # test 2D input; i.e., many poses + num_repeats = 10 + actual_error = compute_relative_errors( # type: ignore + qs_a=np.tile(q_a, (num_repeats, 1)), # type: ignore + qs_b=np.tile(q_b, (num_repeats, 1)), # type: ignore + distances=np.tile(distance, (num_repeats, 1)), # type: ignore robot=robot, ) - np.testing.assert_allclose(actual_error, 0) + np.testing.assert_allclose(actual_error, 0) # type: ignore -def test_optimization(): +def test_optimization() -> None: """Test.""" # init robot model and error wrt nominal actual_robot = Robot.from_parameters(ur10()) - actual_robot.tool.position = [0.1, 0, 0] - actual_robot.kinematic_chain.links[0].a += 0.1 + actual_robot.tool.position = np.array([0.1, 0, 0]) + # FIXME: "Link" has no attribute "a" + # TODO: review abstract inheritance + actual_robot.kinematic_chain.links[0].a += 0.1 # type: ignore # calculate fk - qs = np.tile( + qs = np.tile( # type: ignore np.linspace(start=-np.pi, stop=np.pi, num=100), (len(ur10()), 1) ).transpose() @@ -112,23 +121,23 @@ def test_optimization(): # validate atol = 1e-2 - np.testing.assert_allclose( + np.testing.assert_allclose( # type: ignore actual=result.x, desired=handler.generate_optimization_vector(), atol=atol ) - np.testing.assert_allclose( + np.testing.assert_allclose( # type: ignore actual=handler.robot.kinematic_chain.vector, desired=actual_robot.kinematic_chain.vector, atol=atol, ) - np.testing.assert_allclose( + np.testing.assert_allclose( # type: ignore actual=handler.robot.tool.vector, desired=actual_robot.tool.vector, atol=atol ) - np.testing.assert_allclose( + np.testing.assert_allclose( # type: ignore actual=handler.robot.world_frame, desired=actual_robot.world_frame, atol=atol ) -def test_handler_validate_transform_mask(): +def test_handler_validate_transform_mask() -> None: """Test.""" # test predesigned mask sequence OptimizationHandler(robot=Robot.from_parameters(ur10()), tool_mask=[False] * 6) diff --git a/tests/test_robot.py b/tests/test_robot.py index 52546b8d..1d693871 100644 --- a/tests/test_robot.py +++ b/tests/test_robot.py @@ -3,26 +3,40 @@ import hypothesis import numpy as np +import numpy.typing as npt from hypothesis import given from hypothesis.extra.numpy import arrays from hypothesis.strategies import floats -from pytest import raises +from pytest import fixture, raises from pybotics.errors import PyboticsError +from pybotics.kinematic_chain import MDHKinematicChain from pybotics.predefined_models import ur10 from pybotics.robot import Robot -def test_fk(resources_path: Path): +@fixture(scope="session") +def planar_robot() -> Robot: + """Generate planar robot.""" + return Robot( + MDHKinematicChain.from_parameters( + np.array([[0, 0, 0, 0], [0, 10, 0, 0], [0, 20, 0, 0]]) + ) + ) + + +def test_fk() -> None: """ Test robot. :param robot: :return: """ - # get resource - path = resources_path / "ur10-joints-poses.csv" - data = np.loadtxt(str(path), delimiter=",") + # load test data + data_path = ( + Path(__file__).parent / "resources" + ).resolve() / "ur10-joints-poses.csv" + data = np.loadtxt(str(data_path), delimiter=",") # type: ignore # load robot robot = Robot.from_parameters(ur10()) @@ -37,23 +51,23 @@ def test_fk(resources_path: Path): # test with position argument actual_pose = robot.fk(q=joints) - np.testing.assert_allclose(actual_pose, desired_pose, atol=atol) + np.testing.assert_allclose(actual_pose, desired_pose, atol=atol) # type: ignore # test with internal position attribute robot.joints = joints actual_pose = robot.fk() - np.testing.assert_allclose(actual_pose, desired_pose, atol=atol) + np.testing.assert_allclose(actual_pose, desired_pose, atol=atol) # type: ignore -def test_home_position(): +def test_home_position() -> None: """Test.""" robot = Robot.from_parameters(ur10()) x = np.ones(len(robot)) robot.home_position = x - np.testing.assert_allclose(robot.home_position, x) + np.testing.assert_allclose(robot.home_position, x) # type: ignore -def test_joint_limits(): +def test_joint_limits() -> None: """Test.""" robot = Robot.from_parameters(ur10()) @@ -68,7 +82,7 @@ def test_joint_limits(): robot.joints = robot.joint_limits.copy()[1] + 10 -def test_compute_joint_torques(planar_robot: Robot): +def test_compute_joint_torques(planar_robot: Robot) -> None: """ Test. @@ -78,15 +92,17 @@ def test_compute_joint_torques(planar_robot: Robot): :return: """ # set test force and angles - force = [-100, -200, 0] - moment = [0] * 3 - wrench = force + moment + force = np.array([-100, -200, 0]) + moment = np.zeros(3) + wrench = force + moment # type: npt.NDArray[np.float64] joint_angles = np.deg2rad([30, 60, 0]) # get link lengths + # FIXME: "Link" has no attribute "a" + # TODO: review abstract inheritance lengths = [ - planar_robot.kinematic_chain.links[1].a, - planar_robot.kinematic_chain.links[2].a, + planar_robot.kinematic_chain.links[1].a, # type: ignore + planar_robot.kinematic_chain.links[2].a, # type: ignore ] # calculate expected torques @@ -99,11 +115,11 @@ def test_compute_joint_torques(planar_robot: Robot): # test actual_torques = planar_robot.compute_joint_torques(q=joint_angles, wrench=wrench) - np.testing.assert_allclose(actual_torques, expected_torques) + np.testing.assert_allclose(actual_torques, expected_torques) # type: ignore planar_robot.joints = joint_angles actual_torques = planar_robot.compute_joint_torques(wrench=wrench) - np.testing.assert_allclose(actual_torques, expected_torques) + np.testing.assert_allclose(actual_torques, expected_torques) # type: ignore @given( @@ -115,12 +131,14 @@ def test_compute_joint_torques(planar_robot: Robot): ), ) ) -def test_jacobian_world(q: np.ndarray, planar_robot: Robot): +def test_jacobian_world(q: npt.NDArray[np.float64], planar_robot: Robot) -> None: """Test.""" # get link lengths + # FIXME: "Link" has no attribute "a" + # TODO: review abstract inheritance lengths = [ - planar_robot.kinematic_chain.links[1].a, - planar_robot.kinematic_chain.links[2].a, + planar_robot.kinematic_chain.links[1].a, # type: ignore + planar_robot.kinematic_chain.links[2].a, # type: ignore ] # example from Craig has last joint set to 0 @@ -140,7 +158,7 @@ def test_jacobian_world(q: np.ndarray, planar_robot: Robot): expected[-1, :] = 1 actual = planar_robot.jacobian_world(q) - np.testing.assert_allclose(actual, expected, atol=1e-3) + np.testing.assert_allclose(actual, expected, atol=1e-3) # type: ignore @given( @@ -148,12 +166,14 @@ def test_jacobian_world(q: np.ndarray, planar_robot: Robot): shape=(3,), dtype=float, elements=floats(allow_nan=False, allow_infinity=False) ) ) -def test_jacobian_flange(q: np.ndarray, planar_robot: Robot): +def test_jacobian_flange(q: npt.NDArray[np.float64], planar_robot: Robot) -> None: """Test.""" # get link lengths + # FIXME: "Link" has no attribute "a" + # TODO: review abstract inheritance lengths = [ - planar_robot.kinematic_chain.links[1].a, - planar_robot.kinematic_chain.links[2].a, + planar_robot.kinematic_chain.links[1].a, # type: ignore + planar_robot.kinematic_chain.links[2].a, # type: ignore ] # example from Craig has last joint set to 0 @@ -169,7 +189,7 @@ def test_jacobian_flange(q: np.ndarray, planar_robot: Robot): expected[-1, :] = 1 actual = planar_robot.jacobian_flange(q) - np.testing.assert_allclose(actual, expected, atol=1e-6) + np.testing.assert_allclose(actual, expected, atol=1e-6) # type: ignore @given( @@ -192,7 +212,7 @@ def test_jacobian_flange(q: np.ndarray, planar_robot: Robot): ), ) @hypothesis.settings(deadline=None) -def test_ik(q: np.ndarray, q_offset: np.ndarray): +def test_ik(q: npt.NDArray[np.float64], q_offset: npt.NDArray[np.float64]) -> None: """Test.""" robot = Robot.from_parameters(ur10()) pose = robot.fk(q) @@ -209,22 +229,24 @@ def test_ik(q: np.ndarray, q_offset: np.ndarray): # test the matrix with lower accuracy # rotation components are hard to achieve when x0 isn't good - np.testing.assert_allclose(actual_pose, pose, atol=1) + np.testing.assert_allclose(actual_pose, pose, atol=1) # type: ignore # test the position with higher accuracy desired_position = pose[:-1, -1] actual_position = actual_pose[:-1, -1] - np.testing.assert_allclose(actual_position, desired_position, atol=1e-1) + np.testing.assert_allclose( # type: ignore + actual_position, desired_position, atol=1e-1 + ) -def test_random_joints(): +def test_random_joints() -> None: """Test.""" robot = Robot.from_parameters(ur10()) robot.random_joints() robot.random_joints(in_place=True) -def test_to_json(): +def test_to_json() -> None: """Test.""" robot = Robot.from_parameters(ur10()) robot.to_json() diff --git a/tests/test_tool.py b/tests/test_tool.py index 3ae6395b..95f4f4e6 100644 --- a/tests/test_tool.py +++ b/tests/test_tool.py @@ -5,15 +5,17 @@ from pybotics.tool import Tool -def test_tool(): +def test_tool() -> None: """Test.""" tool = Tool() - cg = [1, 2, 3] + cg = np.array([1, 2, 3]) tool.cg = cg - np.testing.assert_allclose(tool.cg, cg) + np.testing.assert_allclose(tool.cg, cg) # type: ignore - p = [1, 2, 3] + p = np.array([1, 2, 3]) tool.position = p - np.testing.assert_allclose(tool.position, p) - np.testing.assert_allclose(tool.vector, matrix_2_vector(tool.matrix)) + np.testing.assert_allclose(tool.position, p) # type: ignore + np.testing.assert_allclose( # type: ignore + tool.vector, matrix_2_vector(tool.matrix) + )