diff --git a/poetry.lock b/poetry.lock index 18775c5..47cd1a6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -184,11 +184,41 @@ webencodings = "*" css = ["tinycss2 (>=1.1.0)"] dev = ["pip-tools (==6.5.1)", "pytest (==7.1.1)", "flake8 (==4.0.1)", "tox (==3.24.5)", "sphinx (==4.3.2)", "twine (==4.0.0)", "wheel (==0.37.1)", "hashin (==0.17.0)", "black (==22.3.0)", "mypy (==0.942)"] +[[package]] +name = "cachecontrol" +version = "0.12.11" +description = "httplib2 caching for requests" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +lockfile = {version = ">=0.9", optional = true, markers = "extra == \"filecache\""} +msgpack = ">=0.5.2" +requests = "*" + +[package.extras] +filecache = ["lockfile (>=0.9)"] +redis = ["redis (>=2.10.5)"] + +[[package]] +name = "cachy" +version = "0.3.0" +description = "Cachy provides a simple yet effective caching library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.extras] +redis = ["redis (>=3.3.6,<4.0.0)"] +memcached = ["python-memcached (>=1.59,<2.0)"] +msgpack = ["msgpack-python (>=0.5,<0.6)"] + [[package]] name = "certifi" version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." -category = "dev" +category = "main" optional = false python-versions = "*" @@ -215,13 +245,25 @@ python-versions = ">=3.6.1" name = "charset-normalizer" version = "2.0.12" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -category = "dev" +category = "main" optional = false python-versions = ">=3.5.0" [package.extras] unicode_backport = ["unicodedata2"] +[[package]] +name = "cleo" +version = "1.0.0a4" +description = "Cleo allows you to create beautiful and testable command-line interfaces." +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + +[package.dependencies] +crashtest = ">=0.3.1,<0.4.0" +pylev = ">=1.3.0,<2.0.0" + [[package]] name = "click" version = "8.1.2" @@ -267,6 +309,33 @@ tomli = {version = "*", optional = true, markers = "extra == \"toml\""} [package.extras] toml = ["tomli"] +[[package]] +name = "crashtest" +version = "0.3.1" +description = "Manage Python errors with ease" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + +[[package]] +name = "cryptography" +version = "37.0.2" +description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cffi = ">=1.12" + +[package.extras] +docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] +sdist = ["setuptools_rust (>=0.11.4)"] +ssh = ["bcrypt (>=3.1.5)"] +test = ["pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] + [[package]] name = "debugpy" version = "1.6.0" @@ -306,17 +375,17 @@ graph = ["objgraph (>=1.7.2)"] name = "distlib" version = "0.3.4" description = "Distribution utilities" -category = "dev" +category = "main" optional = false python-versions = "*" [[package]] name = "entrypoints" -version = "0.4" +version = "0.3" description = "Discover and load entry points from installed packages." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7" [[package]] name = "fastjsonschema" @@ -333,7 +402,7 @@ devel = ["colorama", "jsonschema", "json-spec", "pylint", "pytest", "pytest-benc name = "filelock" version = "3.6.0" description = "A platform independent file lock." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -378,6 +447,24 @@ python-versions = ">=3.7" gitdb = ">=4.0.1,<5" typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} +[[package]] +name = "html5lib" +version = "1.1" +description = "HTML parser based on the WHATWG HTML specification" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[package.dependencies] +six = ">=1.9" +webencodings = "*" + +[package.extras] +all = ["genshi", "chardet (>=2.2)", "lxml"] +chardet = ["chardet (>=2.2)"] +genshi = ["genshi"] +lxml = ["lxml"] + [[package]] name = "identify" version = "2.4.12" @@ -393,7 +480,7 @@ license = ["ukkonen"] name = "idna" version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" -category = "dev" +category = "main" optional = false python-versions = ">=3.5" @@ -503,7 +590,7 @@ python-versions = "*" name = "isort" version = "5.10.1" description = "A Python utility / library to sort Python imports." -category = "dev" +category = "main" optional = false python-versions = ">=3.6.1,<4.0" @@ -528,6 +615,18 @@ parso = ">=0.8.0,<0.9.0" qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] +[[package]] +name = "jeepney" +version = "0.8.0" +description = "Low-level, pure Python DBus protocol wrapper." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest", "pytest-trio", "pytest-asyncio (>=0.17)", "testpath", "trio", "async-timeout"] +trio = ["trio", "async-generator"] + [[package]] name = "jinja2" version = "3.1.1" @@ -686,6 +785,24 @@ requests = "*" openapi = ["openapi-core (>=0.14.2)", "ruamel.yaml"] test = ["codecov", "ipykernel", "pytest (>=5.3.2)", "pytest-cov", "jupyter-server", "pytest-console-scripts", "strict-rfc3339", "wheel", "openapi-spec-validator (<0.5)", "openapi-core (>=0.14.2)", "ruamel.yaml"] +[[package]] +name = "keyring" +version = "23.5.0" +description = "Store and access your passwords safely." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +importlib-metadata = ">=3.6" +jeepney = {version = ">=0.4.2", markers = "sys_platform == \"linux\""} +pywin32-ctypes = {version = "<0.1.0 || >0.1.0,<0.1.1 || >0.1.1", markers = "sys_platform == \"win32\""} +SecretStorage = {version = ">=3.2", markers = "sys_platform == \"linux\""} + +[package.extras] +docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] + [[package]] name = "lazy-object-proxy" version = "1.7.1" @@ -694,6 +811,14 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "lockfile" +version = "0.12.2" +description = "Platform-independent file locking module" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "markupsafe" version = "2.1.1" @@ -729,6 +854,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "msgpack" +version = "1.0.3" +description = "MessagePack (de)serializer." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "mypy" version = "0.931" @@ -901,14 +1034,14 @@ test = ["pytest", "pytest-tornasync", "pytest-console-scripts"] [[package]] name = "packaging" -version = "21.3" +version = "20.9" description = "Core utilities for Python packages" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [package.dependencies] -pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +pyparsing = ">=2.0.2" [[package]] name = "pandocfilters" @@ -965,11 +1098,22 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "pkginfo" +version = "1.8.2" +description = "Query metadatdata from sdists / bdists / installed packages." +category = "main" +optional = false +python-versions = "*" + +[package.extras] +testing = ["coverage", "nose"] + [[package]] name = "platformdirs" version = "2.5.1" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" @@ -992,6 +1136,57 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "poetry" +version = "1.2.0b1" +description = "Python dependency management and packaging made easy." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +cachecontrol = {version = ">=0.12.9,<0.13.0", extras = ["filecache"]} +cachy = ">=0.3.0,<0.4.0" +cleo = ">=1.0.0a4,<2.0.0" +crashtest = ">=0.3.0,<0.4.0" +entrypoints = ">=0.3,<0.4" +html5lib = ">=1.0,<2.0" +importlib-metadata = {version = ">=1.6.0", markers = "python_version < \"3.8\""} +keyring = ">=21.2.0" +packaging = ">=20.4,<21.0" +pexpect = ">=4.7.0,<5.0.0" +pkginfo = ">=1.5,<2.0" +poetry-core = ">=1.1.0a7,<2.0.0" +requests = ">=2.18,<3.0" +requests-toolbelt = ">=0.9.1,<0.10.0" +shellingham = ">=1.1,<2.0" +tomlkit = ">=0.7.0,<1.0.0" +urllib3 = ">=1.26.0,<2.0.0" +virtualenv = "*" + +[[package]] +name = "poetry-core" +version = "1.1.0a7" +description = "Poetry PEP 517 Build Backend" +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +importlib-metadata = {version = ">=1.7.0", markers = "python_version < \"3.8\""} + +[[package]] +name = "poetry-types" +version = "0.2.2" +description = "A poetry plugin that automatically adds type subs as dependencies like the mypy --install-types command." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" + +[package.dependencies] +colorama = ">=0.4.4,<0.5.0" +poetry = ">=1.2.0b1,<2.0.0" + [[package]] name = "pre-commit" version = "2.18.1" @@ -1076,8 +1271,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pydantic" -version = "1.9.0" -description = "Data validation and settings management using python 3.6 type hinting" +version = "1.9.1" +description = "Data validation and settings management using python type hints" category = "main" optional = false python-versions = ">=3.6.1" @@ -1120,6 +1315,14 @@ category = "main" optional = false python-versions = ">=3.5" +[[package]] +name = "pylev" +version = "1.4.0" +description = "A pure Python Levenshtein implementation that's not freaking GPL'd." +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pylint" version = "2.13.5" @@ -1235,6 +1438,14 @@ category = "main" optional = false python-versions = "*" +[[package]] +name = "pywin32-ctypes" +version = "0.2.0" +description = "" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "pywinpty" version = "2.0.5" @@ -1267,7 +1478,7 @@ py = {version = "*", markers = "implementation_name == \"pypy\""} name = "requests" version = "2.27.1" description = "Python HTTP for Humans." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" @@ -1281,6 +1492,17 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +[[package]] +name = "requests-toolbelt" +version = "0.9.1" +description = "A utility belt for advanced users of python-requests" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +requests = ">=2.0.1,<3.0.0" + [[package]] name = "rich" version = "11.2.0" @@ -1298,6 +1520,18 @@ typing-extensions = {version = ">=3.7.4,<5.0", markers = "python_version < \"3.8 [package.extras] jupyter = ["ipywidgets (>=7.5.1,<8.0.0)"] +[[package]] +name = "secretstorage" +version = "3.3.2" +description = "Python bindings to FreeDesktop.org Secret Service API" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cryptography = ">=2.0" +jeepney = ">=0.6" + [[package]] name = "send2trash" version = "1.8.0" @@ -1311,6 +1545,14 @@ nativelib = ["pyobjc-framework-cocoa", "pywin32"] objc = ["pyobjc-framework-cocoa"] win32 = ["pywin32"] +[[package]] +name = "shellingham" +version = "1.4.0" +description = "Tool to Detect Surrounding Shell" +category = "main" +optional = false +python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" + [[package]] name = "six" version = "1.16.0" @@ -1398,7 +1640,7 @@ test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"] name = "toml" version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" -category = "dev" +category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" @@ -1410,6 +1652,14 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "tomlkit" +version = "0.10.2" +description = "Style preserving TOML library" +category = "main" +optional = false +python-versions = ">=3.6,<4.0" + [[package]] name = "tornado" version = "6.1" @@ -1454,6 +1704,22 @@ dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)"] doc = ["mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "mdx-include (>=1.4.1,<2.0.0)"] test = ["shellingham (>=1.3.0,<2.0.0)", "pytest (>=4.4.0,<5.4.0)", "pytest-cov (>=2.10.0,<3.0.0)", "coverage (>=5.2,<6.0)", "pytest-xdist (>=1.32.0,<2.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "mypy (==0.910)", "black (>=22.3.0,<23.0.0)", "isort (>=5.0.6,<6.0.0)"] +[[package]] +name = "types-pyyaml" +version = "6.0.7" +description = "Typing stubs for PyYAML" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "types-toml" +version = "0.10.7" +description = "Typing stubs for toml" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" version = "4.1.1" @@ -1466,7 +1732,7 @@ python-versions = ">=3.6" name = "urllib3" version = "1.26.9" description = "HTTP library with thread-safe connection pooling, file post, and more." -category = "dev" +category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" @@ -1479,7 +1745,7 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] name = "virtualenv" version = "20.14.1" description = "Virtual Python Environment builder" -category = "dev" +category = "main" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" @@ -1531,6 +1797,14 @@ category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +[[package]] +name = "yml" +version = "0.0.1" +description = "A fast, safe, pure Python YAML and JSON parser" +category = "main" +optional = false +python-versions = "*" + [[package]] name = "zipp" version = "3.8.0" @@ -1546,7 +1820,7 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "76e410cc7b14b2d12c8daec8cb50001e0aee838d4e88f801d68586b3d934c056" +content-hash = "e8ffa0b5eeab70fa1f93badaf1e9921121e1feff93ab9e4735f22e0e360d2239" [metadata.files] anyio = [ @@ -1641,6 +1915,14 @@ bleach = [ {file = "bleach-5.0.0-py3-none-any.whl", hash = "sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1"}, {file = "bleach-5.0.0.tar.gz", hash = "sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565"}, ] +cachecontrol = [ + {file = "CacheControl-0.12.11-py2.py3-none-any.whl", hash = "sha256:2c75d6a8938cb1933c75c50184549ad42728a27e9f6b92fd677c3151aa72555b"}, + {file = "CacheControl-0.12.11.tar.gz", hash = "sha256:a5b9fcc986b184db101aa280b42ecdcdfc524892596f606858e0b7a8b4d9e144"}, +] +cachy = [ + {file = "cachy-0.3.0-py2.py3-none-any.whl", hash = "sha256:338ca09c8860e76b275aff52374330efedc4d5a5e45dc1c5b539c1ead0786fe7"}, + {file = "cachy-0.3.0.tar.gz", hash = "sha256:186581f4ceb42a0bbe040c407da73c14092379b1e4c0e327fdb72ae4a9b269b1"}, +] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, @@ -1705,6 +1987,10 @@ charset-normalizer = [ {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, ] +cleo = [ + {file = "cleo-1.0.0a4-py3-none-any.whl", hash = "sha256:cdd0c3458c15ced3a9f0204b1e53a1b4bee3c56ebcb3ac54c872a56acc657a09"}, + {file = "cleo-1.0.0a4.tar.gz", hash = "sha256:a103a065d031b7d936ee88a6b93086a69bd9c1b40fa2ebfe8c056285a66b481d"}, +] click = [ {file = "click-8.1.2-py3-none-any.whl", hash = "sha256:24e1a4a9ec5bf6299411369b208c1df2188d9eb8d916302fe6bf03faed227f1e"}, {file = "click-8.1.2.tar.gz", hash = "sha256:479707fe14d9ec9a0757618b7a100a0ae4c4e236fac5b7f80ca68028141a1a72"}, @@ -1760,6 +2046,34 @@ coverage = [ {file = "coverage-6.3.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf"}, {file = "coverage-6.3.2.tar.gz", hash = "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9"}, ] +crashtest = [ + {file = "crashtest-0.3.1-py3-none-any.whl", hash = "sha256:300f4b0825f57688b47b6d70c6a31de33512eb2fa1ac614f780939aa0cf91680"}, + {file = "crashtest-0.3.1.tar.gz", hash = "sha256:42ca7b6ce88b6c7433e2ce47ea884e91ec93104a4b754998be498a8e6c3d37dd"}, +] +cryptography = [ + {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:ef15c2df7656763b4ff20a9bc4381d8352e6640cfeb95c2972c38ef508e75181"}, + {file = "cryptography-37.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3c81599befb4d4f3d7648ed3217e00d21a9341a9a688ecdd615ff72ffbed7336"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2bd1096476aaac820426239ab534b636c77d71af66c547b9ddcd76eb9c79e004"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:31fe38d14d2e5f787e0aecef831457da6cec68e0bb09a35835b0b44ae8b988fe"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:093cb351031656d3ee2f4fa1be579a8c69c754cf874206be1d4cf3b542042804"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b281eab51e1b6b6afa525af2bd93c16d49358404f814fe2c2410058623928c"}, + {file = "cryptography-37.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:0cc20f655157d4cfc7bada909dc5cc228211b075ba8407c46467f63597c78178"}, + {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:f8ec91983e638a9bcd75b39f1396e5c0dc2330cbd9ce4accefe68717e6779e0a"}, + {file = "cryptography-37.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:46f4c544f6557a2fefa7ac8ac7d1b17bf9b647bd20b16decc8fbcab7117fbc15"}, + {file = "cryptography-37.0.2-cp36-abi3-win32.whl", hash = "sha256:731c8abd27693323b348518ed0e0705713a36d79fdbd969ad968fbef0979a7e0"}, + {file = "cryptography-37.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:471e0d70201c069f74c837983189949aa0d24bb2d751b57e26e3761f2f782b8d"}, + {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a68254dd88021f24a68b613d8c51d5c5e74d735878b9e32cc0adf19d1f10aaf9"}, + {file = "cryptography-37.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:a7d5137e556cc0ea418dca6186deabe9129cee318618eb1ffecbd35bee55ddc1"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aeaba7b5e756ea52c8861c133c596afe93dd716cbcacae23b80bc238202dc023"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95e590dd70642eb2079d280420a888190aa040ad20f19ec8c6e097e38aa29e06"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1b9362d34363f2c71b7853f6251219298124aa4cc2075ae2932e64c91a3e2717"}, + {file = "cryptography-37.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e53258e69874a306fcecb88b7534d61820db8a98655662a3dd2ec7f1afd9132f"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:1f3bfbd611db5cb58ca82f3deb35e83af34bb8cf06043fa61500157d50a70982"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:419c57d7b63f5ec38b1199a9521d77d7d1754eb97827bbb773162073ccd8c8d4"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:dc26bb134452081859aa21d4990474ddb7e863aa39e60d1592800a8865a702de"}, + {file = "cryptography-37.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3b8398b3d0efc420e777c40c16764d6870bcef2eb383df9c6dbb9ffe12c64452"}, + {file = "cryptography-37.0.2.tar.gz", hash = "sha256:f224ad253cc9cea7568f49077007d2263efa57396a2f2f78114066fd54b5c68e"}, +] debugpy = [ {file = "debugpy-1.6.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:eb1946efac0c0c3d411cea0b5ac772fbde744109fd9520fb0c5a51979faf05ad"}, {file = "debugpy-1.6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e3513399177dd37af4c1332df52da5da1d0c387e5927dc4c0709e26ee7302e8f"}, @@ -1797,8 +2111,8 @@ distlib = [ {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, ] entrypoints = [ - {file = "entrypoints-0.4-py3-none-any.whl", hash = "sha256:f174b5ff827504fd3cd97cc3f8649f3693f51538c7e4bdf3ef002c8429d42f9f"}, - {file = "entrypoints-0.4.tar.gz", hash = "sha256:b706eddaa9218a19ebcd67b56818f05bb27589b1ca9e8d797b74affad4ccacd4"}, + {file = "entrypoints-0.3-py2.py3-none-any.whl", hash = "sha256:589f874b313739ad35be6e0cd7efde2a4e9b6fea91edcc34e58ecbb8dbe56d19"}, + {file = "entrypoints-0.3.tar.gz", hash = "sha256:c70dd71abe5a8c85e55e12c19bd91ccfeec11a6e99044204511f9ed547d48451"}, ] fastjsonschema = [ {file = "fastjsonschema-2.15.3-py3-none-any.whl", hash = "sha256:ddb0b1d8243e6e3abb822bd14e447a89f4ab7439342912d590444831fa00b6a0"}, @@ -1820,6 +2134,10 @@ gitpython = [ {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] +html5lib = [ + {file = "html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d"}, + {file = "html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f"}, +] identify = [ {file = "identify-2.4.12-py2.py3-none-any.whl", hash = "sha256:5f06b14366bd1facb88b00540a1de05b69b310cbc2654db3c7e07fa3a4339323"}, {file = "identify-2.4.12.tar.gz", hash = "sha256:3f3244a559290e7d3deb9e9adc7b33594c1bc85a9dd82e0f1be519bf12a1ec17"}, @@ -1860,6 +2178,10 @@ jedi = [ {file = "jedi-0.18.1-py2.py3-none-any.whl", hash = "sha256:637c9635fcf47945ceb91cd7f320234a7be540ded6f3e99a50cb6febdfd1ba8d"}, {file = "jedi-0.18.1.tar.gz", hash = "sha256:74137626a64a99c8eb6ae5832d99b3bdd7d29a3850fe2aa80a4126b2a7d949ab"}, ] +jeepney = [ + {file = "jeepney-0.8.0-py3-none-any.whl", hash = "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755"}, + {file = "jeepney-0.8.0.tar.gz", hash = "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806"}, +] jinja2 = [ {file = "Jinja2-3.1.1-py3-none-any.whl", hash = "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119"}, {file = "Jinja2-3.1.1.tar.gz", hash = "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"}, @@ -1896,6 +2218,10 @@ jupyterlab-server = [ {file = "jupyterlab_server-2.12.0-py3-none-any.whl", hash = "sha256:db5d234955c5c2684f77a064345712f071acf7df31f0d8c31b420b33b09d6472"}, {file = "jupyterlab_server-2.12.0.tar.gz", hash = "sha256:00e0f4b4c399f55938323ea10cf92d915288fe12753e35d1069f6ca08b72abbf"}, ] +keyring = [ + {file = "keyring-23.5.0-py3-none-any.whl", hash = "sha256:b0d28928ac3ec8e42ef4cc227822647a19f1d544f21f96457965dc01cf555261"}, + {file = "keyring-23.5.0.tar.gz", hash = "sha256:9012508e141a80bd1c0b6778d5c610dd9f8c464d75ac6774248500503f972fb9"}, +] lazy-object-proxy = [ {file = "lazy-object-proxy-1.7.1.tar.gz", hash = "sha256:d609c75b986def706743cdebe5e47553f4a5a1da9c5ff66d76013ef396b5a8a4"}, {file = "lazy_object_proxy-1.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bb8c5fd1684d60a9902c60ebe276da1f2281a318ca16c1d0a96db28f62e9166b"}, @@ -1935,6 +2261,10 @@ lazy-object-proxy = [ {file = "lazy_object_proxy-1.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:677ea950bef409b47e51e733283544ac3d660b709cfce7b187f5ace137960d61"}, {file = "lazy_object_proxy-1.7.1-pp37.pp38-none-any.whl", hash = "sha256:d66906d5785da8e0be7360912e99c9188b70f52c422f9fc18223347235691a84"}, ] +lockfile = [ + {file = "lockfile-0.12.2-py2.py3-none-any.whl", hash = "sha256:6c3cb24f344923d30b2785d5ad75182c8ea7ac1b6171b08657258ec7429d50fa"}, + {file = "lockfile-0.12.2.tar.gz", hash = "sha256:6aed02de03cba24efabcd600b30540140634fc06cfa603822d508d5361e9f799"}, +] markupsafe = [ {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, @@ -1989,6 +2319,42 @@ mistune = [ {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, ] +msgpack = [ + {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:96acc674bb9c9be63fa8b6dabc3248fdc575c4adc005c440ad02f87ca7edd079"}, + {file = "msgpack-1.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2c3ca57c96c8e69c1a0d2926a6acf2d9a522b41dc4253a8945c4c6cd4981a4e3"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b0a792c091bac433dfe0a70ac17fc2087d4595ab835b47b89defc8bbabcf5c73"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1c58cdec1cb5fcea8c2f1771d7b5fec79307d056874f746690bd2bdd609ab147"}, + {file = "msgpack-1.0.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2f97c0f35b3b096a330bb4a1a9247d0bd7e1f3a2eba7ab69795501504b1c2c39"}, + {file = "msgpack-1.0.3-cp310-cp310-win32.whl", hash = "sha256:36a64a10b16c2ab31dcd5f32d9787ed41fe68ab23dd66957ca2826c7f10d0b85"}, + {file = "msgpack-1.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:c1ba333b4024c17c7591f0f372e2daa3c31db495a9b2af3cf664aef3c14354f7"}, + {file = "msgpack-1.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c2140cf7a3ec475ef0938edb6eb363fa704159e0bf71dde15d953bacc1cf9d7d"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f4c22717c74d44bcd7af353024ce71c6b55346dad5e2cc1ddc17ce8c4507c6b"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d733a15ade190540c703de209ffbc42a3367600421b62ac0c09fde594da6ec"}, + {file = "msgpack-1.0.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7e03b06f2982aa98d4ddd082a210c3db200471da523f9ac197f2828e80e7770"}, + {file = "msgpack-1.0.3-cp36-cp36m-win32.whl", hash = "sha256:3d875631ecab42f65f9dce6f55ce6d736696ced240f2634633188de2f5f21af9"}, + {file = "msgpack-1.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:40fb89b4625d12d6027a19f4df18a4de5c64f6f3314325049f219683e07e678a"}, + {file = "msgpack-1.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eef0cf8db3857b2b556213d97dd82de76e28a6524853a9beb3264983391dc1a"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d8c332f53ffff01953ad25131272506500b14750c1d0ce8614b17d098252fbc"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c0903bd93cbd34653dd63bbfcb99d7539c372795201f39d16fdfde4418de43a"}, + {file = "msgpack-1.0.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf1e6bfed4860d72106f4e0a1ab519546982b45689937b40257cfd820650b920"}, + {file = "msgpack-1.0.3-cp37-cp37m-win32.whl", hash = "sha256:d02cea2252abc3756b2ac31f781f7a98e89ff9759b2e7450a1c7a0d13302ff50"}, + {file = "msgpack-1.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2f30dd0dc4dfe6231ad253b6f9f7128ac3202ae49edd3f10d311adc358772dba"}, + {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f201d34dc89342fabb2a10ed7c9a9aaaed9b7af0f16a5923f1ae562b31258dea"}, + {file = "msgpack-1.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bb87f23ae7d14b7b3c21009c4b1705ec107cb21ee71975992f6aca571fb4a42a"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a3a5c4b16e9d0edb823fe54b59b5660cc8d4782d7bf2c214cb4b91a1940a8ef"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f74da1e5fcf20ade12c6bf1baa17a2dc3604958922de8dc83cbe3eff22e8b611"}, + {file = "msgpack-1.0.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:73a80bd6eb6bcb338c1ec0da273f87420829c266379c8c82fa14c23fb586cfa1"}, + {file = "msgpack-1.0.3-cp38-cp38-win32.whl", hash = "sha256:9fce00156e79af37bb6db4e7587b30d11e7ac6a02cb5bac387f023808cd7d7f4"}, + {file = "msgpack-1.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:9b6f2d714c506e79cbead331de9aae6837c8dd36190d02da74cb409b36162e8a"}, + {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:89908aea5f46ee1474cc37fbc146677f8529ac99201bc2faf4ef8edc023c2bf3"}, + {file = "msgpack-1.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:973ad69fd7e31159eae8f580f3f707b718b61141838321c6fa4d891c4a2cca52"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da24375ab4c50e5b7486c115a3198d207954fe10aaa5708f7b65105df09109b2"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a598d0685e4ae07a0672b59792d2cc767d09d7a7f39fd9bd37ff84e060b1a996"}, + {file = "msgpack-1.0.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4c309a68cb5d6bbd0c50d5c71a25ae81f268c2dc675c6f4ea8ab2feec2ac4e2"}, + {file = "msgpack-1.0.3-cp39-cp39-win32.whl", hash = "sha256:494471d65b25a8751d19c83f1a482fd411d7ca7a3b9e17d25980a74075ba0e88"}, + {file = "msgpack-1.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:f01b26c2290cbd74316990ba84a14ac3d599af9cebefc543d241a66e785cf17d"}, + {file = "msgpack-1.0.3.tar.gz", hash = "sha256:51fdc7fb93615286428ee7758cecc2f374d5ff363bdd884c7ea622a7a327a81e"}, +] mypy = [ {file = "mypy-0.931-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3c5b42d0815e15518b1f0990cff7a705805961613e701db60387e6fb663fe78a"}, {file = "mypy-0.931-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c89702cac5b302f0c5d33b172d2b55b5df2bede3344a2fbed99ff96bddb2cf00"}, @@ -2048,8 +2414,8 @@ notebook-shim = [ {file = "notebook_shim-0.1.0.tar.gz", hash = "sha256:7897e47a36d92248925a2143e3596f19c60597708f7bef50d81fcd31d7263e85"}, ] packaging = [ - {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, - {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, + {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"}, + {file = "packaging-20.9.tar.gz", hash = "sha256:5b327ac1320dc863dca72f4514ecc086f31186744b84a230374cc1fd776feae5"}, ] pandocfilters = [ {file = "pandocfilters-1.5.0-py2.py3-none-any.whl", hash = "sha256:33aae3f25fd1a026079f5d27bdd52496f0e0803b3469282162bafdcbdf6ef14f"}, @@ -2075,6 +2441,10 @@ pickleshare = [ {file = "pickleshare-0.7.5-py2.py3-none-any.whl", hash = "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56"}, {file = "pickleshare-0.7.5.tar.gz", hash = "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca"}, ] +pkginfo = [ + {file = "pkginfo-1.8.2-py2.py3-none-any.whl", hash = "sha256:c24c487c6a7f72c66e816ab1796b96ac6c3d14d49338293d2141664330b55ffc"}, + {file = "pkginfo-1.8.2.tar.gz", hash = "sha256:542e0d0b6750e2e21c20179803e40ab50598d8066d51097a0e382cba9eb02bff"}, +] platformdirs = [ {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, @@ -2083,6 +2453,18 @@ pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +poetry = [ + {file = "poetry-1.2.0b1-py3-none-any.whl", hash = "sha256:e3d68c88492550c48df10c738e962f1f770ad71e715bab878a46f527e1ce81d2"}, + {file = "poetry-1.2.0b1.tar.gz", hash = "sha256:26cf8d309a74fff25d768219c2215a989a530acab886c01de3db07ab70bc7abf"}, +] +poetry-core = [ + {file = "poetry-core-1.1.0a7.tar.gz", hash = "sha256:4622ae680842ac9b1b9c3b0e8dc467c2e291d1a5c434b6bd413907a2e5571d92"}, + {file = "poetry_core-1.1.0a7-py3-none-any.whl", hash = "sha256:724e8b5368f270461e622396305d0c2e760ec9d4c14d072e6b944da9384c67de"}, +] +poetry-types = [ + {file = "poetry-types-0.2.2.tar.gz", hash = "sha256:ab234515cf1d8c1f77a25fb1b0a8ff85845e3f39a2a9fc7dbecf7984c9e07a80"}, + {file = "poetry_types-0.2.2-py3-none-any.whl", hash = "sha256:518ee6e28f00a2719e3a04a076f6926a8adcb8518d3e1b61b625cc8ab65df512"}, +] pre-commit = [ {file = "pre_commit-2.18.1-py2.py3-none-any.whl", hash = "sha256:02226e69564ebca1a070bd1f046af866aa1c318dbc430027c50ab832ed2b73f2"}, {file = "pre_commit-2.18.1.tar.gz", hash = "sha256:5d445ee1fa8738d506881c5d84f83c62bb5be6b2838e32207433647e8e5ebe10"}, @@ -2146,41 +2528,41 @@ pycparser = [ {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] pydantic = [ - {file = "pydantic-1.9.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb23bcc093697cdea2708baae4f9ba0e972960a835af22560f6ae4e7e47d33f5"}, - {file = "pydantic-1.9.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1d5278bd9f0eee04a44c712982343103bba63507480bfd2fc2790fa70cd64cf4"}, - {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab624700dc145aa809e6f3ec93fb8e7d0f99d9023b713f6a953637429b437d37"}, - {file = "pydantic-1.9.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c8d7da6f1c1049eefb718d43d99ad73100c958a5367d30b9321b092771e96c25"}, - {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3c3b035103bd4e2e4a28da9da7ef2fa47b00ee4a9cf4f1a735214c1bcd05e0f6"}, - {file = "pydantic-1.9.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3011b975c973819883842c5ab925a4e4298dffccf7782c55ec3580ed17dc464c"}, - {file = "pydantic-1.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:086254884d10d3ba16da0588604ffdc5aab3f7f09557b998373e885c690dd398"}, - {file = "pydantic-1.9.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0fe476769acaa7fcddd17cadd172b156b53546ec3614a4d880e5d29ea5fbce65"}, - {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8e9dcf1ac499679aceedac7e7ca6d8641f0193c591a2d090282aaf8e9445a46"}, - {file = "pydantic-1.9.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e4c28f30e767fd07f2ddc6f74f41f034d1dd6bc526cd59e63a82fe8bb9ef4c"}, - {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c86229333cabaaa8c51cf971496f10318c4734cf7b641f08af0a6fbf17ca3054"}, - {file = "pydantic-1.9.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:c0727bda6e38144d464daec31dff936a82917f431d9c39c39c60a26567eae3ed"}, - {file = "pydantic-1.9.0-cp36-cp36m-win_amd64.whl", hash = "sha256:dee5ef83a76ac31ab0c78c10bd7d5437bfdb6358c95b91f1ba7ff7b76f9996a1"}, - {file = "pydantic-1.9.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:d9c9bdb3af48e242838f9f6e6127de9be7063aad17b32215ccc36a09c5cf1070"}, - {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ee7e3209db1e468341ef41fe263eb655f67f5c5a76c924044314e139a1103a2"}, - {file = "pydantic-1.9.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0b6037175234850ffd094ca77bf60fb54b08b5b22bc85865331dd3bda7a02fa1"}, - {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b2571db88c636d862b35090ccf92bf24004393f85c8870a37f42d9f23d13e032"}, - {file = "pydantic-1.9.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8b5ac0f1c83d31b324e57a273da59197c83d1bb18171e512908fe5dc7278a1d6"}, - {file = "pydantic-1.9.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bbbc94d0c94dd80b3340fc4f04fd4d701f4b038ebad72c39693c794fd3bc2d9d"}, - {file = "pydantic-1.9.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e0896200b6a40197405af18828da49f067c2fa1f821491bc8f5bde241ef3f7d7"}, - {file = "pydantic-1.9.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7bdfdadb5994b44bd5579cfa7c9b0e1b0e540c952d56f627eb227851cda9db77"}, - {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:574936363cd4b9eed8acdd6b80d0143162f2eb654d96cb3a8ee91d3e64bf4cf9"}, - {file = "pydantic-1.9.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c556695b699f648c58373b542534308922c46a1cda06ea47bc9ca45ef5b39ae6"}, - {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:f947352c3434e8b937e3aa8f96f47bdfe6d92779e44bb3f41e4c213ba6a32145"}, - {file = "pydantic-1.9.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5e48ef4a8b8c066c4a31409d91d7ca372a774d0212da2787c0d32f8045b1e034"}, - {file = "pydantic-1.9.0-cp38-cp38-win_amd64.whl", hash = "sha256:96f240bce182ca7fe045c76bcebfa0b0534a1bf402ed05914a6f1dadff91877f"}, - {file = "pydantic-1.9.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:815ddebb2792efd4bba5488bc8fde09c29e8ca3227d27cf1c6990fc830fd292b"}, - {file = "pydantic-1.9.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6c5b77947b9e85a54848343928b597b4f74fc364b70926b3c4441ff52620640c"}, - {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c68c3bc88dbda2a6805e9a142ce84782d3930f8fdd9655430d8576315ad97ce"}, - {file = "pydantic-1.9.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a79330f8571faf71bf93667d3ee054609816f10a259a109a0738dac983b23c3"}, - {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f5a64b64ddf4c99fe201ac2724daada8595ada0d102ab96d019c1555c2d6441d"}, - {file = "pydantic-1.9.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a733965f1a2b4090a5238d40d983dcd78f3ecea221c7af1497b845a9709c1721"}, - {file = "pydantic-1.9.0-cp39-cp39-win_amd64.whl", hash = "sha256:2cc6a4cb8a118ffec2ca5fcb47afbacb4f16d0ab8b7350ddea5e8ef7bcc53a16"}, - {file = "pydantic-1.9.0-py3-none-any.whl", hash = "sha256:085ca1de245782e9b46cefcf99deecc67d418737a1fd3f6a4f511344b613a5b3"}, - {file = "pydantic-1.9.0.tar.gz", hash = "sha256:742645059757a56ecd886faf4ed2441b9c0cd406079c2b4bee51bcc3fbcd510a"}, + {file = "pydantic-1.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8098a724c2784bf03e8070993f6d46aa2eeca031f8d8a048dff277703e6e193"}, + {file = "pydantic-1.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c320c64dd876e45254bdd350f0179da737463eea41c43bacbee9d8c9d1021f11"}, + {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18f3e912f9ad1bdec27fb06b8198a2ccc32f201e24174cec1b3424dda605a310"}, + {file = "pydantic-1.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c11951b404e08b01b151222a1cb1a9f0a860a8153ce8334149ab9199cd198131"}, + {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8bc541a405423ce0e51c19f637050acdbdf8feca34150e0d17f675e72d119580"}, + {file = "pydantic-1.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e565a785233c2d03724c4dc55464559639b1ba9ecf091288dd47ad9c629433bd"}, + {file = "pydantic-1.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:a4a88dcd6ff8fd47c18b3a3709a89adb39a6373f4482e04c1b765045c7e282fd"}, + {file = "pydantic-1.9.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:447d5521575f18e18240906beadc58551e97ec98142266e521c34968c76c8761"}, + {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:985ceb5d0a86fcaa61e45781e567a59baa0da292d5ed2e490d612d0de5796918"}, + {file = "pydantic-1.9.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:059b6c1795170809103a1538255883e1983e5b831faea6558ef873d4955b4a74"}, + {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:d12f96b5b64bec3f43c8e82b4aab7599d0157f11c798c9f9c528a72b9e0b339a"}, + {file = "pydantic-1.9.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ae72f8098acb368d877b210ebe02ba12585e77bd0db78ac04a1ee9b9f5dd2166"}, + {file = "pydantic-1.9.1-cp36-cp36m-win_amd64.whl", hash = "sha256:79b485767c13788ee314669008d01f9ef3bc05db9ea3298f6a50d3ef596a154b"}, + {file = "pydantic-1.9.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:494f7c8537f0c02b740c229af4cb47c0d39840b829ecdcfc93d91dcbb0779892"}, + {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0f047e11febe5c3198ed346b507e1d010330d56ad615a7e0a89fae604065a0e"}, + {file = "pydantic-1.9.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:969dd06110cb780da01336b281f53e2e7eb3a482831df441fb65dd30403f4608"}, + {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:177071dfc0df6248fd22b43036f936cfe2508077a72af0933d0c1fa269b18537"}, + {file = "pydantic-1.9.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9bcf8b6e011be08fb729d110f3e22e654a50f8a826b0575c7196616780683380"}, + {file = "pydantic-1.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a955260d47f03df08acf45689bd163ed9df82c0e0124beb4251b1290fa7ae728"}, + {file = "pydantic-1.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9ce157d979f742a915b75f792dbd6aa63b8eccaf46a1005ba03aa8a986bde34a"}, + {file = "pydantic-1.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0bf07cab5b279859c253d26a9194a8906e6f4a210063b84b433cf90a569de0c1"}, + {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d93d4e95eacd313d2c765ebe40d49ca9dd2ed90e5b37d0d421c597af830c195"}, + {file = "pydantic-1.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1542636a39c4892c4f4fa6270696902acb186a9aaeac6f6cf92ce6ae2e88564b"}, + {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a9af62e9b5b9bc67b2a195ebc2c2662fdf498a822d62f902bf27cccb52dbbf49"}, + {file = "pydantic-1.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fe4670cb32ea98ffbf5a1262f14c3e102cccd92b1869df3bb09538158ba90fe6"}, + {file = "pydantic-1.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:9f659a5ee95c8baa2436d392267988fd0f43eb774e5eb8739252e5a7e9cf07e0"}, + {file = "pydantic-1.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b83ba3825bc91dfa989d4eed76865e71aea3a6ca1388b59fc801ee04c4d8d0d6"}, + {file = "pydantic-1.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1dd8fecbad028cd89d04a46688d2fcc14423e8a196d5b0a5c65105664901f810"}, + {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02eefd7087268b711a3ff4db528e9916ac9aa18616da7bca69c1871d0b7a091f"}, + {file = "pydantic-1.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7eb57ba90929bac0b6cc2af2373893d80ac559adda6933e562dcfb375029acee"}, + {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:4ce9ae9e91f46c344bec3b03d6ee9612802682c1551aaf627ad24045ce090761"}, + {file = "pydantic-1.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:72ccb318bf0c9ab97fc04c10c37683d9eea952ed526707fabf9ac5ae59b701fd"}, + {file = "pydantic-1.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:61b6760b08b7c395975d893e0b814a11cf011ebb24f7d869e7118f5a339a82e1"}, + {file = "pydantic-1.9.1-py3-none-any.whl", hash = "sha256:4988c0f13c42bfa9ddd2fe2f569c9d54646ce84adc5de84228cfe83396f3bd58"}, + {file = "pydantic-1.9.1.tar.gz", hash = "sha256:1ed987c3ff29fff7fd8c3ea3a3ea877ad310aae2ef9889a119e22d3f2db0691a"}, ] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, @@ -2194,6 +2576,10 @@ pygments = [ {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, ] +pylev = [ + {file = "pylev-1.4.0-py2.py3-none-any.whl", hash = "sha256:7b2e2aa7b00e05bb3f7650eb506fc89f474f70493271a35c242d9a92188ad3dd"}, + {file = "pylev-1.4.0.tar.gz", hash = "sha256:9e77e941042ad3a4cc305dcdf2b2dec1aec2fbe3dd9015d2698ad02b173006d1"}, +] pylint = [ {file = "pylint-2.13.5-py3-none-any.whl", hash = "sha256:c149694cfdeaee1aa2465e6eaab84c87a881a7d55e6e93e09466be7164764d1e"}, {file = "pylint-2.13.5.tar.gz", hash = "sha256:dab221658368c7a05242e673c275c488670144123f4bd262b2777249c1c0de9b"}, @@ -2259,6 +2645,10 @@ pywin32 = [ {file = "pywin32-303-cp39-cp39-win32.whl", hash = "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352"}, {file = "pywin32-303-cp39-cp39-win_amd64.whl", hash = "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34"}, ] +pywin32-ctypes = [ + {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"}, + {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"}, +] pywinpty = [ {file = "pywinpty-2.0.5-cp310-none-win_amd64.whl", hash = "sha256:f86c76e2881c37e69678cbbf178109f8da1fa8584db24d58e1b9369b0276cfcb"}, {file = "pywinpty-2.0.5-cp37-none-win_amd64.whl", hash = "sha256:ff9b52f182650cfdf3db1b264a6fe0963eb9d996a7a1fa843ac406c1e32111f8"}, @@ -2354,14 +2744,26 @@ requests = [ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, ] +requests-toolbelt = [ + {file = "requests-toolbelt-0.9.1.tar.gz", hash = "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0"}, + {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, +] rich = [ {file = "rich-11.2.0-py3-none-any.whl", hash = "sha256:d5f49ad91fb343efcae45a2b2df04a9755e863e50413623ab8c9e74f05aee52b"}, {file = "rich-11.2.0.tar.gz", hash = "sha256:1a6266a5738115017bb64a66c59c717e7aa047b3ae49a011ede4abdeffc6536e"}, ] +secretstorage = [ + {file = "SecretStorage-3.3.2-py3-none-any.whl", hash = "sha256:755dc845b6ad76dcbcbc07ea3da75ae54bb1ea529eb72d15f83d26499a5df319"}, + {file = "SecretStorage-3.3.2.tar.gz", hash = "sha256:0a8eb9645b320881c222e827c26f4cfcf55363e8b374a021981ef886657a912f"}, +] send2trash = [ {file = "Send2Trash-1.8.0-py3-none-any.whl", hash = "sha256:f20eaadfdb517eaca5ce077640cb261c7d2698385a6a0f072a4a5447fd49fa08"}, {file = "Send2Trash-1.8.0.tar.gz", hash = "sha256:d2c24762fd3759860a0aff155e45871447ea58d2be6bdd39b5c8f966a0c99c2d"}, ] +shellingham = [ + {file = "shellingham-1.4.0-py2.py3-none-any.whl", hash = "sha256:536b67a0697f2e4af32ab176c00a50ac2899c5a05e0d8e2dadac8e58888283f9"}, + {file = "shellingham-1.4.0.tar.gz", hash = "sha256:4855c2458d6904829bd34c299f11fdeed7cfefbf8a2c522e4caea6cd76b3171e"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -2402,6 +2804,10 @@ tomli = [ {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] +tomlkit = [ + {file = "tomlkit-0.10.2-py3-none-any.whl", hash = "sha256:905cf92c2111ef80d355708f47ac24ad1b6fc2adc5107455940088c9bbecaedb"}, + {file = "tomlkit-0.10.2.tar.gz", hash = "sha256:30d54c0b914e595f3d10a87888599eab5321a2a69abc773bbefff51599b72db6"}, +] tornado = [ {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, @@ -2479,6 +2885,14 @@ typer = [ {file = "typer-0.4.1-py3-none-any.whl", hash = "sha256:e8467f0ebac0c81366c2168d6ad9f888efdfb6d4e1d3d5b4a004f46fa444b5c3"}, {file = "typer-0.4.1.tar.gz", hash = "sha256:5646aef0d936b2c761a10393f0384ee6b5c7fe0bb3e5cd710b17134ca1d99cff"}, ] +types-pyyaml = [ + {file = "types-PyYAML-6.0.7.tar.gz", hash = "sha256:59480cf44595d836aaae050f35e3c39f197f3a833679ef3978d97aa9f2fb7def"}, + {file = "types_PyYAML-6.0.7-py3-none-any.whl", hash = "sha256:7b273a34f32af9910cf9405728c9d2ad3afc4be63e4048091a1a73d76681fe67"}, +] +types-toml = [ + {file = "types-toml-0.10.7.tar.gz", hash = "sha256:a567fe2614b177d537ad99a661adc9bfc8c55a46f95e66370a4ed2dd171335f9"}, + {file = "types_toml-0.10.7-py3-none-any.whl", hash = "sha256:05a8da4bfde2f1ee60e90c7071c063b461f74c63a9c3c1099470c08d6fa58615"}, +] typing-extensions = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, @@ -2569,6 +2983,9 @@ wrapt = [ {file = "wrapt-1.14.0-cp39-cp39-win_amd64.whl", hash = "sha256:bb36fbb48b22985d13a6b496ea5fb9bb2a076fea943831643836c9f6febbcfdc"}, {file = "wrapt-1.14.0.tar.gz", hash = "sha256:8323a43bd9c91f62bb7d4be74cc9ff10090e7ef820e27bfe8815c57e68261311"}, ] +yml = [ + {file = "yml-0.0.1.tar.gz", hash = "sha256:5168010f8cfd91fbcda1d0bffae869a1117050bf8cf0e909d4f00ccd9bd80ab9"}, +] zipp = [ {file = "zipp-3.8.0-py3-none-any.whl", hash = "sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099"}, {file = "zipp-3.8.0.tar.gz", hash = "sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad"}, diff --git a/pynblint/core_models.py b/pynblint/core_models.py index 16bd463..82c888f 100644 --- a/pynblint/core_models.py +++ b/pynblint/core_models.py @@ -1,4 +1,5 @@ import ast +import copy import os import re import tempfile @@ -6,12 +7,13 @@ from abc import ABC from enum import Enum from pathlib import Path -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Set import git import nbconvert import nbformat import rich +import toml from nbformat.notebooknode import NotebookNode from rich.abc import RichRenderable from rich.columns import Columns @@ -19,8 +21,10 @@ from rich.padding import Padding from rich.panel import Panel from rich.syntax import Syntax +from yaml import safe_load from .config import CellRenderingMode, settings +from .exceptions import InvalidRequirementsFileError from .rich_extensions import NotebookMarkdown @@ -36,6 +40,7 @@ def __init__(self, path: Path): # Extracted content self.notebooks: List[Notebook] = [] # List of Notebook objects + self.declared_requirements: Set = self._parse_requirements() def retrieve_notebooks(self): @@ -80,6 +85,114 @@ def large_file_paths(self) -> List[Path]: large_files.append(file_path) return large_files + def _parse_requirements(self) -> Set: + + supported_requirement_formats = [ + "requirements.txt", + "environment.yaml", + "environment.yml", + "pyproject.toml", + "setup.py", + "Pipfile", + ] + paths: List[Path] = [] + + for root, _, files in os.walk(self.path): + for f in files: + if f in supported_requirement_formats: + paths.append(Path(root) / f) + + declared_requirements = set() + for path in paths: + if path.name == "requirements.txt": + declared_requirements.update(self._get_requirements_from_txt(path)) + elif path.name == "environment.yaml" or path.name == "environment.yml": + declared_requirements.update(self._get_requirements_from_yaml(path)) + elif path.name == "pyproject.toml": + declared_requirements.update(self._get_requirements_from_toml(path)) + elif path.name == "setup.py": + declared_requirements.update(self._get_requirements_from_setup(path)) + elif path.name == "Pipfile": + declared_requirements.update(self._get_requirements_from_pipfile(path)) + + return declared_requirements + + @staticmethod + def _get_requirements_from_txt(path: Path) -> set: + with open(path, "r") as fi: + lines = [ + line + for line in fi.readlines() + if line.strip() != "" and not line.startswith("#") + ] + return {re.split(r"[><=]=", dependency)[0].rstrip() for dependency in lines} + + @staticmethod + def _get_requirements_from_toml(path: Path) -> set: + try: + parsed_toml = toml.load(path) + except Exception: + raise InvalidRequirementsFileError( + "Project requirements could not be parsed in `pyproject.toml`: " + "invalid toml syntax." + ) + return set(parsed_toml["tool"]["poetry"]["dependencies"].keys()) + + @staticmethod + def _get_requirements_from_yaml(path: Path) -> set: + with open(path, "r") as fi: + try: + parsed_yaml = safe_load(fi.read()) + except Exception: + raise InvalidRequirementsFileError( + "Project requirements could not be parsed from `environment.yml`: " + "invalid yaml syntax." + ) + + raw_deps = [] + for item in parsed_yaml["dependencies"]: + if type(item) is str: + raw_deps.append(item) + else: + pip_dependencies = item.get("pip") + if pip_dependencies: + raw_deps.extend(pip_dependencies) + return {re.split(r"[><=]?=", req)[0] for req in raw_deps} + + @staticmethod + def _get_requirements_from_pipfile(path: Path) -> set: + try: + parsed_pipfile = toml.load(path) + except Exception: + raise InvalidRequirementsFileError( + "Project requirements could not be parsed from `Pipfile`: " + "invalid toml syntax." + ) + return set(parsed_pipfile["packages"].keys()) + + @staticmethod + def _get_requirements_from_setup(path: Path) -> set: + with open(path, "r") as fi: + try: + parsed_setup_file = ast.parse(fi.read()) + except Exception: + raise InvalidRequirementsFileError( + "Project requirements could not be parsed from `setup.py`: " + "invalid Python syntax." + ) + requirements: Set = set() + for node in ast.walk(parsed_setup_file): + if isinstance(node, ast.Call) and node.func.id == "setup": # type: ignore + for keyword in node.keywords: + if keyword.arg == "install_requires": + raw_requirements_list = ast.literal_eval(keyword.value) + processed_requirements_list = [ + re.split(r"[><=]=", req)[0].rstrip() + for req in raw_requirements_list + ] + requirements.update(processed_requirements_list) + return requirements + class LocalRepository(Repository): """ @@ -127,7 +240,6 @@ class GitHubRepository(Repository): """ def __init__(self, github_url: str): - self.url = github_url # Clone the repo in a temp directory @@ -284,8 +396,13 @@ def __init__(self, path: Path, repository: Optional[Repository] = None): self.non_executed = all([cell.non_executed for cell in self.code_cells]) # Convert the notebook to a Python script + nb_dict_no_magic = copy.deepcopy(self.nb_dict) + for cell in nb_dict_no_magic.cells: + cell.source = "\n".join( + [line for line in cell.source.splitlines() if not line.startswith("%")] + ) python_exporter = nbconvert.PythonExporter() - self.script, _ = python_exporter.from_notebook_node(self.nb_dict) + self.script, _ = python_exporter.from_notebook_node(nb_dict_no_magic) # Extract the Python abstract syntax tree # (or set `has_invalid_python_syntax` to True) @@ -295,6 +412,10 @@ def __init__(self, path: Path, repository: Optional[Repository] = None): except SyntaxError: self.has_invalid_python_syntax = True + # Get the set of imported Python packages + if not self.has_invalid_python_syntax: + self.imported_packages: Set = self._get_imported_packages() + @property def code_cells(self) -> List[Cell]: code_cells = [cell for cell in self.cells if cell.cell_type == CellType.CODE] @@ -313,6 +434,36 @@ def initial_cells(self) -> List[Cell]: def final_cells(self) -> List[Cell]: return self.cells[-settings.final_cells :] # noqa: E203 + def _get_imported_packages(self) -> Set: + """Builds the set of packages and modules imported in the notebook. + + Sice it relies on the ``ast`` module, this function works only for notebooks + with a valid Python syntax. Therefore, a ``ValueError`` exception is raised if + this function is invoked on a notebook containing syntactic Python errors. + + Returns: + Set: the set of packages and modules imported in the notebook. + """ + + if self.has_invalid_python_syntax: + raise ValueError( + "Imported packages cannot be parsed in notebooks with invalid " + "Python syntax." + ) + + imported_packages: Set = set() + for node in ast.walk(self.ast): + if isinstance(node, ast.Import): + for name in node.names: + imported_packages.add(name.name.split(".")[0]) + elif isinstance(node, ast.ImportFrom): + if node.level > 0: + # Relative imports always refer to the current package. + continue + if node.module: + imported_packages.add(node.module.split(".")[0]) + return imported_packages + def __len__(self) -> int: return len(self.cells) diff --git a/pynblint/exceptions.py b/pynblint/exceptions.py index bc9f4fd..fb584aa 100644 --- a/pynblint/exceptions.py +++ b/pynblint/exceptions.py @@ -2,3 +2,9 @@ class ExportFormatNotSupportedError(ValueError): def __init__(self, message) -> None: self.message = message super().__init__(self.message) + + +class InvalidRequirementsFileError(SyntaxError): + def __init__(self, message) -> None: + self.message = message + super().__init__(self.message) diff --git a/pynblint/nb_linting.py b/pynblint/nb_linting.py index 9e7ea88..bd5574a 100644 --- a/pynblint/nb_linting.py +++ b/pynblint/nb_linting.py @@ -1,8 +1,11 @@ """Linting functions for notebooks.""" import ast import re +import sys from typing import List, Pattern +from isort import stdlibs + from . import lint_register as register from .config import settings from .core_models import Cell, CellType, Notebook @@ -230,6 +233,59 @@ def non_executed_notebook(notebook: Notebook) -> bool: return notebook.non_executed +def undeclared_dependencies(notebook: Notebook) -> bool: + """Check if the dependencies of the notebook are properly declared. + + This check is performed only when notebooks belong to a repository. + + Args: + notebook (Notebook): the notebook to be analyzed + + Returns: + bool: ``True`` if the notebook contains import statements referred to + packages that are not part of the core Python libraries and are not declared + in a dependency management file: ``requirements.txt``, ``setup.py``, + ``environment.yml``, ``pyproject.toml``, or ``Pipfile``; ``False`` otherwise. + """ + + # Check that the notebook is part of a repository (if not, exit) + if not notebook.repository or notebook.has_invalid_python_syntax: + return False + else: + + # Set of python core modules and packages + minor_python_version = sys.version_info.minor + + if minor_python_version == 7: + stdlib = stdlibs.py37.stdlib + elif minor_python_version == 8: + stdlib = stdlibs.py38.stdlib + elif minor_python_version == 9: + stdlib = stdlibs.py39.stdlib + elif minor_python_version == 10: + stdlib = stdlibs.py310.stdlib + else: + raise Exception( + "Python version not supported: Pynblint currently supports " + "Python versions from 3.7 to 3.10." + ) + + core_dependecies = set(stdlib) + + # Modules and packages imported in the notebook, excluding core ones + external_dependencies = notebook.imported_packages - core_dependecies + + # Modules and packages that are not declared in dependency management files + undeclared_dependencies = ( + external_dependencies - notebook.repository.declared_requirements + ) + + if len(undeclared_dependencies) > 0: + return True + else: + return False + + # ========== # # CELL LEVEL # # ========== # @@ -385,6 +441,16 @@ def long_multiline_python_comment(notebook: Notebook) -> List[Cell]: "that all cells are executed.", linting_function=non_executed_notebook, ), + LintDefinition( + slug="undeclared-dependencies", + description="The notebook has external dependencies that are not declared " + "in a dependency management file (e.g., `requirements.txt`, `setup.py`, " + "`environment.yml`, `pyproject.toml`, or `Pipfile`)", + recommendation="Use a dependency management tool (e.g., `pip`, `conda`, or " + "`Poetry`) to declare your dependencies or to refresh the set " + "of declared dependencies.", + linting_function=undeclared_dependencies, + ), ] cell_level_lints: List[LintDefinition] = [ @@ -403,7 +469,7 @@ def long_multiline_python_comment(notebook: Notebook) -> List[Cell]: show_details=False, ), LintDefinition( - slug="long_multiline_python_comment", + slug="long-multiline-python-comment", description="One or more code cells in this notebook contain Python comments " f"of {settings.max_multiline_python_comment} or more consecutive lines.", recommendation="For improved notebook readability, prefer using Markdown " diff --git a/pyproject.toml b/pyproject.toml index 69a2e5b..7616ad9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,12 @@ rich = "^11.1.0" typer = "^0.4.0" pydantic = {extras = ["dotenv"], version = "^1.9.0"} ipython = "<8" +isort = "^5.10.1" +types-toml = "^0.10.7" +yml = "^0.0.1" +types-PyYAML = "^6.0.7" +toml = "^0.10.2" +poetry-types = "^0.2.2" [tool.poetry.dev-dependencies] jupyterlab = "^3.2.8" @@ -57,3 +63,10 @@ max-line-length = 88 [tool.bandit] exclude_dirs = ["tests"] + +[[tool.mypy.overrides]] + module = [ + "toml", + "yaml" + ] + ignore_missing_imports = true diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/fixtures/LongNotebook.ipynb b/tests/fixtures/LongNotebook.ipynb index dc16d7b..c13c288 100644 --- a/tests/fixtures/LongNotebook.ipynb +++ b/tests/fixtures/LongNotebook.ipynb @@ -1,5 +1,43 @@ { "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "%%capture\n", + "# File system manangement\n", + "import time, psutil, os\n", + "\n", + "# Progress bar for loops\n", + "!pip install tqdm\n", + "from tqdm import tqdm\n", + "\n", + "# Mathematical functions\n", + "import math\n", + "\n", + "# Data manipulation\n", + "import numpy as np\n", + "import pandas as pd\n", + "\n", + "# Plotting and visualization\n", + "import matplotlib.pyplot as plt\n", + "%matplotlib inline\n", + "import matplotlib.patches as mpatches\n", + "from .local_package.local_module import Locally_defined_class\n", + "\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "from matplotlib.colors import ListedColormap\n", + "from matplotlib import cm\n", + "from mpl_toolkits.mplot3d.axes3d import get_test_data\n", + "\n", + "import seaborn as sns\n", + "sns.set_theme()\n", + "import plotly.graph_objects as go\n", + "from plotly.subplots import make_subplots" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/tests/fixtures/requirement_files/conda/environment_1.yaml b/tests/fixtures/requirement_files/conda/environment_1.yaml new file mode 100644 index 0000000..4c4524d --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_1.yaml @@ -0,0 +1,23 @@ +name: enhancing +package: example +version: 0.0.1 +py_version: 2.7.10 +channels: + - pytorch + - defaults +dependencies: + - python=3.8.5 + - pip=20.3 + - cudatoolkit=10.2 + - pytorch=1.7.1 + - torchvision=0.8.2 + - numpy=1.19.2 + - pip: + - ftfy==6.1.1 + - lpips==0.1.4 + - regex==2021.10.8 + - pytorch-lightning==1.6.3 + - lmdb==1.0.0 + - wandb==0.12.7 + - git+https://github.com/openai/CLIP.git + - kornia==0.5.11 diff --git a/tests/fixtures/requirement_files/conda/environment_1_yaml.txt b/tests/fixtures/requirement_files/conda/environment_1_yaml.txt new file mode 100644 index 0000000..839f2b5 --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_1_yaml.txt @@ -0,0 +1,14 @@ +python +pip +cudatoolkit +pytorch +torchvision +numpy +ftfy +lpips +regex +pytorch-lightning +lmdb +wandb +git+https://github.com/openai/CLIP.git +kornia diff --git a/tests/fixtures/requirement_files/conda/environment_2.yaml b/tests/fixtures/requirement_files/conda/environment_2.yaml new file mode 100644 index 0000000..d9a92dc --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_2.yaml @@ -0,0 +1,14 @@ +name: Project1_ML +channels: + - conda-forge + - defaults +dependencies: + - anyio=3.6.1=py38haa244fe_0 + - argon2-cffi=21.3.0=pyhd8ed1ab_0 + - asgiref=3.5.2=pyhd8ed1ab_0 + - asttokens=2.0.5=pyhd8ed1ab_0 + - attrs=21.4.0=pyhd8ed1ab_0 + - babel=2.10.1=pyhd8ed1ab_0 + - backports=1.0=py_2 + - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 + - beautifulsoup4=4.11.1=pyha770c72_0 diff --git a/tests/fixtures/requirement_files/conda/environment_2_yaml.txt b/tests/fixtures/requirement_files/conda/environment_2_yaml.txt new file mode 100644 index 0000000..2ed7787 --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_2_yaml.txt @@ -0,0 +1,9 @@ +anyio +argon2-cffi +asgiref +asttokens +attrs +babel +backports +backports.functools_lru_cache +beautifulsoup4 diff --git a/tests/fixtures/requirement_files/conda/environment_3.yaml b/tests/fixtures/requirement_files/conda/environment_3.yaml new file mode 100644 index 0000000..56622ee --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_3.yaml @@ -0,0 +1,18 @@ +name: rhometa +channels: + - bioconda + - conda-forge +dependencies: + - python>=3.9 + - numpy=1.21 + - pandas + - scipy + - seaborn + - numba + - tqdm + - bcftools + - pysam>=0.17 + - freebayes>=1.3.5 + - samtools>=1.15 + - openssl>=1.1.1n + - biopython diff --git a/tests/fixtures/requirement_files/conda/environment_3_yaml.txt b/tests/fixtures/requirement_files/conda/environment_3_yaml.txt new file mode 100644 index 0000000..0085a90 --- /dev/null +++ b/tests/fixtures/requirement_files/conda/environment_3_yaml.txt @@ -0,0 +1,13 @@ +python +numpy +pandas +scipy +seaborn +numba +tqdm +bcftools +pysam +freebayes +samtools +openssl +biopython diff --git a/tests/fixtures/requirement_files/pip/requirements_1.txt b/tests/fixtures/requirement_files/pip/requirements_1.txt new file mode 100644 index 0000000..945d8be --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_1.txt @@ -0,0 +1,10 @@ +alabaster==0.7.12 +attrs==21.4.0 +certifi==2021.10.8 +charset-normalizer==2.0.11 +commonmark==0.9.1 +idna==3.3 +imagesize==1.3.0 +ipython-genutils==0.2.0 +git+https://github.com/path/to/package-two@releases/tag/v3.7.1#egg=package-two +Jinja2==3.0.3 diff --git a/tests/fixtures/requirement_files/pip/requirements_1_txt.txt b/tests/fixtures/requirement_files/pip/requirements_1_txt.txt new file mode 100644 index 0000000..44e6b78 --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_1_txt.txt @@ -0,0 +1,10 @@ +alabaster +attrs +certifi +charset-normalizer +commonmark +idna +imagesize +ipython-genutils +git+https://github.com/path/to/package-two@releases/tag/v3.7.1#egg=package-two +Jinja2 diff --git a/tests/fixtures/requirement_files/pip/requirements_2.txt b/tests/fixtures/requirement_files/pip/requirements_2.txt new file mode 100644 index 0000000..d035cb3 --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_2.txt @@ -0,0 +1,13 @@ +numpy <= 1.21.5 +pandas <= 1.3.3 +sklearn == 0.0 +Pillow == 8.4.0 +streamlit <= 1.9.0 +cufflinks <= 0.17.3 +matplotlib <= 3.4.3 +pickle-mixin==1.0.2 +pickleshare <= 0.7.5 +seaborn == 0.11.2 +scikit-learn==1.1.0 +plotly==5.7.0 +plotly-express==0.4.0 diff --git a/tests/fixtures/requirement_files/pip/requirements_2_txt.txt b/tests/fixtures/requirement_files/pip/requirements_2_txt.txt new file mode 100644 index 0000000..9a08563 --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_2_txt.txt @@ -0,0 +1,13 @@ +numpy +pandas +sklearn +Pillow +streamlit +cufflinks +matplotlib +pickle-mixin +pickleshare +seaborn +scikit-learn +plotly +plotly-express diff --git a/tests/fixtures/requirement_files/pip/requirements_3.txt b/tests/fixtures/requirement_files/pip/requirements_3.txt new file mode 100644 index 0000000..98b84c6 --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_3.txt @@ -0,0 +1,20 @@ +## Python==3.9.12 + +rubika +requests +urlib + +# Streamlit +streamlit==1.9.0 +watchdog==2.1.7 + +# FastAPI +fastapi==0.77.1 +uvicorn==0.17.6 +email-validator==1.2.1 + +# lxml +lxml==4.8.0 + +# tynybb +tinydb==4.7.0 diff --git a/tests/fixtures/requirement_files/pip/requirements_3_txt.txt b/tests/fixtures/requirement_files/pip/requirements_3_txt.txt new file mode 100644 index 0000000..c09cf22 --- /dev/null +++ b/tests/fixtures/requirement_files/pip/requirements_3_txt.txt @@ -0,0 +1,10 @@ +rubika +requests +urlib +streamlit +watchdog +fastapi +uvicorn +email-validator +lxml +tinydb diff --git a/tests/fixtures/requirement_files/pipfile/Pipfile_1 b/tests/fixtures/requirement_files/pipfile/Pipfile_1 new file mode 100644 index 0000000..66fb730 --- /dev/null +++ b/tests/fixtures/requirement_files/pipfile/Pipfile_1 @@ -0,0 +1,24 @@ +[[source]] +verify_ssl = true +url = "https://pypi.org/simple" +name = "pypi" + +[dev-packages] + +[requires] +python_version = "3.8" + +[packages] +cryptography = "~=37.0.2" +django = "~=2.2.26" +"boto3" = "~=1.23.0" +django-storages = "~=1.12" +psycopg2 = "<2.9" +waitress = "~=2.1.1" +whitenoise = "~=6.0.0" +djangorestframework = "~=3.13.1" +django-bootstrap4 = "~=22.1" +packaging = "*" +drf-yasg = "~=1.20.0" +pytest = "==7.1.2" +factory-boy = "==3.2.1" diff --git a/tests/fixtures/requirement_files/pipfile/Pipfile_1.txt b/tests/fixtures/requirement_files/pipfile/Pipfile_1.txt new file mode 100644 index 0000000..f26d157 --- /dev/null +++ b/tests/fixtures/requirement_files/pipfile/Pipfile_1.txt @@ -0,0 +1,13 @@ +cryptography +django +boto3 +django-storages +psycopg2 +waitress +whitenoise +djangorestframework +django-bootstrap4 +packaging +drf-yasg +pytest +factory-boy diff --git a/tests/fixtures/requirement_files/pipfile/Pipfile_2 b/tests/fixtures/requirement_files/pipfile/Pipfile_2 new file mode 100644 index 0000000..28eb13c --- /dev/null +++ b/tests/fixtures/requirement_files/pipfile/Pipfile_2 @@ -0,0 +1,18 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +django = "*" +djangorestframework = "*" + +[dev-packages] +django-debug-toolbar = "*" +django-extensions = "*" +flake8 = "*" +autopep8 = "*" +pre-commit = "*" + +[requires] +python_version = "3.8" diff --git a/tests/fixtures/requirement_files/pipfile/Pipfile_2.txt b/tests/fixtures/requirement_files/pipfile/Pipfile_2.txt new file mode 100644 index 0000000..6e5370e --- /dev/null +++ b/tests/fixtures/requirement_files/pipfile/Pipfile_2.txt @@ -0,0 +1,2 @@ +django +djangorestframework diff --git a/tests/fixtures/requirement_files/poetry/pyproject_1.toml b/tests/fixtures/requirement_files/poetry/pyproject_1.toml new file mode 100644 index 0000000..dd1471c --- /dev/null +++ b/tests/fixtures/requirement_files/poetry/pyproject_1.toml @@ -0,0 +1,16 @@ +[tool.poetry] +name = "repl_python3_discord-bot" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = "^3.8" +discord = "^1.7.3" +Flask = "^2.0.2" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/tests/fixtures/requirement_files/poetry/pyproject_1_toml.txt b/tests/fixtures/requirement_files/poetry/pyproject_1_toml.txt new file mode 100644 index 0000000..039dd8d --- /dev/null +++ b/tests/fixtures/requirement_files/poetry/pyproject_1_toml.txt @@ -0,0 +1,3 @@ +python +discord +Flask diff --git a/tests/fixtures/requirement_files/poetry/pyproject_2.toml b/tests/fixtures/requirement_files/poetry/pyproject_2.toml new file mode 100644 index 0000000..7616ad9 --- /dev/null +++ b/tests/fixtures/requirement_files/poetry/pyproject_2.toml @@ -0,0 +1,72 @@ +[tool.poetry] +name = "pynblint" +version = "0.1.5" +description = "A linter for Jupyter notebooks written in Python." +authors = ["Luigi Quaranta "] +license = "MIT License" +readme = "README.md" +homepage = "https://pynblint.readthedocs.io/en/latest/" +repository = "https://github.com/collab-uniba/pynblint" +keywords = ["Jupyter", "notebook", "lint", "quality assurance", "static analysis"] +classifiers = [ + "Environment :: Console", + "Framework :: Jupyter", + "Topic :: Software Development :: Quality Assurance" +] +include = [ + "LICENSE" +] + +[tool.poetry.scripts] +pynblint = "pynblint.main:app" + +[tool.poetry.dependencies] +python = "^3.7" +nbformat = "^5.1.3" +nbconvert = "^6.4.0" +GitPython = "^3.1.26" +rich = "^11.1.0" +typer = "^0.4.0" +pydantic = {extras = ["dotenv"], version = "^1.9.0"} +ipython = "<8" +isort = "^5.10.1" +types-toml = "^0.10.7" +yml = "^0.0.1" +types-PyYAML = "^6.0.7" +toml = "^0.10.2" +poetry-types = "^0.2.2" + +[tool.poetry.dev-dependencies] +jupyterlab = "^3.2.8" +black = "^22.3.0" +pytest = "^6.2.5" +bandit = "^1.7.1" +mypy = "^0.931" +flake8 = "^4.0.1" +isort = "^5.10.1" +pylint = "^2.12.2" +pre-commit = "^2.17.0" +pydocstyle = "^6.1.1" +coverage = "^6.2" +pytest-cov = "^3.0.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.isort] +profile = "black" +skip_gitignore = true + +[tool.flake8] +max-line-length = 88 + +[tool.bandit] +exclude_dirs = ["tests"] + +[[tool.mypy.overrides]] + module = [ + "toml", + "yaml" + ] + ignore_missing_imports = true diff --git a/tests/fixtures/requirement_files/poetry/pyproject_2_toml.txt b/tests/fixtures/requirement_files/poetry/pyproject_2_toml.txt new file mode 100644 index 0000000..e4fb4c5 --- /dev/null +++ b/tests/fixtures/requirement_files/poetry/pyproject_2_toml.txt @@ -0,0 +1,14 @@ +python +nbformat +nbconvert +GitPython +rich +typer +pydantic +ipython +isort +types-toml +yml +types-PyYAML +toml +poetry-types diff --git a/tests/fixtures/requirement_files/setup/setup_1.py b/tests/fixtures/requirement_files/setup/setup_1.py new file mode 100644 index 0000000..2c29dea --- /dev/null +++ b/tests/fixtures/requirement_files/setup/setup_1.py @@ -0,0 +1,12 @@ +from setuptools import find_packages, setup + +setup( + name="MyPackageName", + version="1.0.0", + url="https://github.com/mypackage.git", + author="Author Name", + author_email="author@gmail.com", + description="Description of my package", + packages=find_packages(), + install_requires=["pandas", "numpy >= 1.11.1", "matplotlib == 1.5.1"], +) diff --git a/tests/fixtures/requirement_files/setup/setup_1_py.txt b/tests/fixtures/requirement_files/setup/setup_1_py.txt new file mode 100644 index 0000000..c6406d6 --- /dev/null +++ b/tests/fixtures/requirement_files/setup/setup_1_py.txt @@ -0,0 +1,3 @@ +pandas +numpy +matplotlib diff --git a/tests/fixtures/requirement_files/setup/setup_2.py b/tests/fixtures/requirement_files/setup/setup_2.py new file mode 100644 index 0000000..88053a6 --- /dev/null +++ b/tests/fixtures/requirement_files/setup/setup_2.py @@ -0,0 +1,19 @@ +from setuptools import setup + +setup( + name="autorate", + version="0.1.0", + author="Eshan King, Davis Weaver", + author_email="", + packages=["autorate", "autorate.test", "autorate.test.data"], + install_requires=[ + "pandas", + "pytest", + "scipy", + "matplotlib", + "numpy", + "importlib_resources", + ], + include_package_data=True, + package_data={"": ["data/*.csv"]}, +) diff --git a/tests/fixtures/requirement_files/setup/setup_2_py.txt b/tests/fixtures/requirement_files/setup/setup_2_py.txt new file mode 100644 index 0000000..b08d336 --- /dev/null +++ b/tests/fixtures/requirement_files/setup/setup_2_py.txt @@ -0,0 +1,6 @@ +pandas +pytest +scipy +matplotlib +numpy +importlib_resources diff --git a/tests/fixtures/test_repo/UntitledNoDuplicates/Repo_notebook.ipynb b/tests/fixtures/test_repo/UntitledNoDuplicates/Repo_notebook.ipynb index 0607720..344efde 100644 --- a/tests/fixtures/test_repo/UntitledNoDuplicates/Repo_notebook.ipynb +++ b/tests/fixtures/test_repo/UntitledNoDuplicates/Repo_notebook.ipynb @@ -10,7 +10,9 @@ { "cell_type": "code", "execution_count": 1, - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "name": "stdout", @@ -23,6 +25,24 @@ "source": [ "print(\"This is a sample notebook.\")" ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas,requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import Pillow, moviepy, pendulum" + ] } ], "metadata": { @@ -30,7 +50,7 @@ "hash": "610977a96a05ebdfcd21668b3e0264e9d8733b5a64fd7351248315a5aac06b05" }, "kernelspec": { - "display_name": "Python 3.7.12 ('pynblint-SWRkmaaF-py3.7')", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -44,9 +64,8 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.12" - }, - "orig_nbformat": 4 + "version": "3.7.4" + } }, "nbformat": 4, "nbformat_minor": 2 diff --git a/tests/fixtures/test_repo/UntitledNoDuplicates/requirements.txt b/tests/fixtures/test_repo/UntitledNoDuplicates/requirements.txt new file mode 100644 index 0000000..101ea0d --- /dev/null +++ b/tests/fixtures/test_repo/UntitledNoDuplicates/requirements.txt @@ -0,0 +1,16 @@ +numpy <= 1.21.5 +pandas <= 1.3.3 +sklearn == 0.0 +Pillow == 8.4.0 +streamlit <= 1.9.0 +cufflinks <= 0.17.3 +matplotlib <= 3.4.3 +pickle-mixin==1.0.2 +pickleshare <= 0.7.5 +seaborn == 0.11.2 +scikit-learn==1.1.0 +plotly==5.7.0 +plotly-express==0.4.0 +moviepy == 1.0.3 +pendulum == 2.0 +requests == 2.7.0 diff --git a/tests/fixtures/test_repo/dependenciesNotDeclared/Repo_notebook2.ipynb b/tests/fixtures/test_repo/dependenciesNotDeclared/Repo_notebook2.ipynb new file mode 100644 index 0000000..344efde --- /dev/null +++ b/tests/fixtures/test_repo/dependenciesNotDeclared/Repo_notebook2.ipynb @@ -0,0 +1,72 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "A sample notebook inside a sample repo." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": false + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "This is a sample notebook.\n" + ] + } + ], + "source": [ + "print(\"This is a sample notebook.\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas,requests" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import Pillow, moviepy, pendulum" + ] + } + ], + "metadata": { + "interpreter": { + "hash": "610977a96a05ebdfcd21668b3e0264e9d8733b5a64fd7351248315a5aac06b05" + }, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.7.4" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/unit/test_core_models.py b/tests/unit/test_core_models.py index 22e076d..96e45f6 100644 --- a/tests/unit/test_core_models.py +++ b/tests/unit/test_core_models.py @@ -1,13 +1,35 @@ from pathlib import Path +from typing import Dict, Set import pytest -from pynblint.core_models import LocalRepository, Notebook +from pynblint.core_models import LocalRepository, Notebook, Repository if __name__ == "__main__": pytest.main() +# ************* # +# TEST NOTEBOOK # +# ************* # + + +@pytest.fixture(scope="module") +def notebooks() -> Dict[str, Notebook]: + nb1 = Notebook(Path("tests/fixtures", "FullNotebook2.ipynb")) + nb2 = Notebook(Path("tests/fixtures", "Untitled2.ipynb")) + nb3 = Notebook( + Path("tests/fixtures", "titanic-gradientboostingclassifier-Copy1.ipynb") + ) + nb4 = Notebook(Path("tests/fixtures", "LongNotebook.ipynb")) + return { + "FullNotebook2.ipynb": nb1, + "Untitled2.ipynb": nb2, + "titanic-gradientboostingclassifier-Copy1.ipynb": nb3, + "LongNotebook.ipynb": nb4, + } + + def test_standalone_notebook_constructor(): """Tests the contruction of a standalone ``Notebook`` object.""" @@ -26,3 +48,308 @@ def test_notebook_from_repo_constructor(): assert notebook.repository is not None assert notebook.repository.path == repo_path + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("FullNotebook2.ipynb", {"os"}), + ( + "titanic-gradientboostingclassifier-Copy1.ipynb", + { + "pandas", + "numpy", + "matplotlib", + "seaborn", + "missingno", + "pycaret", + "warnings", + }, + ), + ("Untitled2.ipynb", set()), + ( + "LongNotebook.ipynb", + { + "time", + "psutil", + "os", + "tqdm", + "math", + "numpy", + "pandas", + "matplotlib", + "mpl_toolkits", + "seaborn", + "plotly", + }, + ), + ], +) +def test_get_imported_packages(test_input, expected, notebooks): + assert Notebook._get_imported_packages(notebooks[test_input]) == expected + + +# *************** # +# TEST REPOSITORY # +# *************** # + +# Parsing project requirements from: +# environment.yaml + + +@pytest.fixture() +def environment_yaml() -> Dict[str, Path]: + + BASE_PATH = Path("tests/fixtures/requirement_files/conda/") + environment_yaml_1: Path = BASE_PATH / "environment_1.yaml" + environment_yaml_2: Path = BASE_PATH / "environment_2.yaml" + environment_yaml_3: Path = BASE_PATH / "environment_3.yaml" + + return { + "environment_1.yaml": environment_yaml_1, + "environment_2.yaml": environment_yaml_2, + "environment_3.yaml": environment_yaml_3, + } + + +@pytest.fixture() +def environment_yaml_keys() -> Dict[str, Set[str]]: + BASE_PATH = Path("tests/fixtures/requirement_files/conda/") + environment_yaml_1_keys_file_path: Path = BASE_PATH / "environment_1_yaml.txt" + with open(environment_yaml_1_keys_file_path, "r") as f: + environment_yaml_1_keys = {line.rstrip() for line in f.readlines()} + + environment_yaml_2_keys_file_path: Path = BASE_PATH / "environment_2_yaml.txt" + with open(environment_yaml_2_keys_file_path, "r") as f: + environment_yaml_2_keys = {line.rstrip() for line in f.readlines()} + + environment_yaml_3_keys_file_path: Path = BASE_PATH / "environment_3_yaml.txt" + with open(environment_yaml_3_keys_file_path, "r") as f: + environment_yaml_3_keys = {line.rstrip() for line in f.readlines()} + + return { + "environment_1.yaml": environment_yaml_1_keys, + "environment_2.yaml": environment_yaml_2_keys, + "environment_3.yaml": environment_yaml_3_keys, + } + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("environment_1.yaml", "environment_1.yaml"), + ("environment_2.yaml", "environment_2.yaml"), + ("environment_3.yaml", "environment_3.yaml"), + ], +) +def test_get_requirements_from_yaml( + test_input, expected, environment_yaml, environment_yaml_keys +): + assert ( + Repository._get_requirements_from_yaml(environment_yaml[test_input]) + == environment_yaml_keys[expected] + ) + + +# Parsing project requirements from: +# requirements.txt + + +@pytest.fixture() +def requirements_txt() -> Dict[str, Path]: + + BASE_PATH = Path("tests/fixtures/requirement_files/pip/") + requirements_txt_1: Path = BASE_PATH / "requirements_1.txt" + requirements_txt_2: Path = BASE_PATH / "requirements_2.txt" + requirements_txt_3: Path = BASE_PATH / "requirements_3.txt" + + return { + "requirements_1.txt": requirements_txt_1, + "requirements_2.txt": requirements_txt_2, + "requirements_3.txt": requirements_txt_3, + } + + +@pytest.fixture() +def requirements_txt_keys() -> Dict[str, Set[str]]: + BASE_PATH = Path("tests/fixtures/requirement_files/pip/") + requirements_txt_1_keys_file_path: Path = BASE_PATH / "requirements_1_txt.txt" + with open(requirements_txt_1_keys_file_path, "r") as f: + requirements_txt_1_keys = {line.rstrip() for line in f.readlines()} + + requirements_txt_2_keys_file_path: Path = BASE_PATH / "requirements_2_txt.txt" + with open(requirements_txt_2_keys_file_path, "r") as f: + requirements_txt_2_keys = {line.rstrip() for line in f.readlines()} + + requirements_txt_3_keys_file_path: Path = BASE_PATH / "requirements_3_txt.txt" + with open(requirements_txt_3_keys_file_path, "r") as f: + requirements_txt_3_keys = {line.rstrip() for line in f.readlines()} + + return { + "requirements_1.txt": requirements_txt_1_keys, + "requirements_2.txt": requirements_txt_2_keys, + "requirements_3.txt": requirements_txt_3_keys, + } + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("requirements_1.txt", "requirements_1.txt"), + ("requirements_2.txt", "requirements_2.txt"), + ("requirements_3.txt", "requirements_3.txt"), + ], +) +def test_get_requirements_from_txt( + test_input, expected, requirements_txt, requirements_txt_keys +): + assert ( + Repository._get_requirements_from_txt(requirements_txt[test_input]) + == requirements_txt_keys[expected] + ) + + +# Parsing project requirements from: +# pyproject.toml + + +@pytest.fixture() +def pyproject_toml() -> Dict[str, Path]: + + BASE_PATH = Path("tests/fixtures/requirement_files/poetry/") + pyproject_toml_1: Path = BASE_PATH / "pyproject_1.toml" + pyproject_toml_2: Path = BASE_PATH / "pyproject_2.toml" + + return { + "pyproject_1.toml": pyproject_toml_1, + "pyproject_2.toml": pyproject_toml_2, + } + + +@pytest.fixture() +def pyproject_toml_keys() -> Dict[str, Set[str]]: + BASE_PATH = Path("tests/fixtures/requirement_files/poetry/") + pyproject_toml_1_keys_file_path: Path = BASE_PATH / "pyproject_1_toml.txt" + with open(pyproject_toml_1_keys_file_path, "r") as f: + pyproject_toml_1_keys = {line.rstrip() for line in f.readlines()} + + pyproject_toml_2_keys_file_path: Path = BASE_PATH / "pyproject_2_toml.txt" + with open(pyproject_toml_2_keys_file_path, "r") as f: + pyproject_toml_2_keys = {line.rstrip() for line in f.readlines()} + + return { + "pyproject_1.toml": pyproject_toml_1_keys, + "pyproject_2.toml": pyproject_toml_2_keys, + } + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("pyproject_1.toml", "pyproject_1.toml"), + ("pyproject_2.toml", "pyproject_2.toml"), + ], +) +def test_get_requirements_from_toml( + test_input, expected, pyproject_toml, pyproject_toml_keys +): + assert ( + Repository._get_requirements_from_toml(pyproject_toml[test_input]) + == pyproject_toml_keys[expected] + ) + + +# Parsing project requirements from: +# setup.py + + +@pytest.fixture() +def setup_py() -> Dict[str, Path]: + + BASE_PATH = Path("tests/fixtures/requirement_files/setup/") + setup_py_1: Path = BASE_PATH / "setup_1.py" + setup_py_2: Path = BASE_PATH / "setup_2.py" + + return { + "setup_1.py": setup_py_1, + "setup_2.py": setup_py_2, + } + + +@pytest.fixture() +def setup_py_keys() -> Dict[str, Set[str]]: + BASE_PATH = Path("tests/fixtures/requirement_files/setup/") + setup_py_1_keys_file_path: Path = BASE_PATH / "setup_1_py.txt" + with open(setup_py_1_keys_file_path, "r") as f: + setup_py_1_keys = {line.rstrip() for line in f.readlines()} + + setup_py_2_keys_file_path: Path = BASE_PATH / "setup_2_py.txt" + with open(setup_py_2_keys_file_path, "r") as f: + setup_py_2_keys = {line.rstrip() for line in f.readlines()} + + return { + "setup_1.py": setup_py_1_keys, + "setup_2.py": setup_py_2_keys, + } + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("setup_1.py", "setup_1.py"), + ("setup_2.py", "setup_2.py"), + ], +) +def test_get_requirements_from_setup(test_input, expected, setup_py, setup_py_keys): + assert ( + Repository._get_requirements_from_setup(setup_py[test_input]) + == setup_py_keys[expected] + ) + + +# Parsing project requirements from: +# Pipfile + + +@pytest.fixture() +def pipfile() -> Dict[str, Path]: + + BASE_PATH = Path("tests/fixtures/requirement_files/pipfile/") + pipfile_1: Path = BASE_PATH / "Pipfile_1" + pipfile_2: Path = BASE_PATH / "Pipfile_2" + + return { + "Pipfile_1": pipfile_1, + "Pipfile_2": pipfile_2, + } + + +@pytest.fixture() +def pipfile_keys() -> Dict[str, Set[str]]: + BASE_PATH = Path("tests/fixtures/requirement_files/pipfile/") + pipfile_1_keys_file_path: Path = BASE_PATH / "Pipfile_1.txt" + with open(pipfile_1_keys_file_path, "r") as f: + pipfile_1_keys = {line.rstrip() for line in f.readlines()} + + pipfile_2_keys_file_path: Path = BASE_PATH / "Pipfile_2.txt" + with open(pipfile_2_keys_file_path, "r") as f: + pipfile_2_keys = {line.rstrip() for line in f.readlines()} + + return { + "Pipfile_1": pipfile_1_keys, + "Pipfile_2": pipfile_2_keys, + } + + +@pytest.mark.parametrize( + "test_input,expected", + [ + ("Pipfile_1", "Pipfile_1"), + ("Pipfile_2", "Pipfile_2"), + ], +) +def test_get_requirements_from_pipfile(test_input, expected, pipfile, pipfile_keys): + assert ( + Repository._get_requirements_from_pipfile(pipfile[test_input]) + == pipfile_keys[expected] + ) diff --git a/tests/unit/test_nb_linting_functions.py b/tests/unit/test_nb_linting_functions.py index ee03683..2461c86 100644 --- a/tests/unit/test_nb_linting_functions.py +++ b/tests/unit/test_nb_linting_functions.py @@ -5,7 +5,7 @@ from pynblint import nb_linting from pynblint.config import settings -from pynblint.core_models import Cell, Notebook +from pynblint.core_models import Cell, LocalRepository, Notebook @pytest.fixture(scope="module") @@ -19,6 +19,7 @@ def notebooks() -> Dict[str, Notebook]: nb7 = Notebook(Path("tests/fixtures", "Untitled.ipynb")) nb8 = Notebook(Path("tests/fixtures", "InvalidSyntax.ipynb")) nb9 = Notebook(Path("tests/fixtures", "NonExecutedNotebook.ipynb")) + return { "FullNotebook2.ipynb": nb1, "FullNotebookFullNotebookFullNotebook.ipynb": nb2, @@ -32,6 +33,19 @@ def notebooks() -> Dict[str, Notebook]: } +@pytest.fixture(scope="module") +def notebooks_from_repositories() -> Dict[str, Notebook]: + repo_fixtures_base_path: Path = Path("tests/fixtures/test_repo") + repo1 = LocalRepository(repo_fixtures_base_path / "UntitledNoDuplicates") + repo2 = LocalRepository(repo_fixtures_base_path / "dependenciesNotDeclared") + nb1 = repo1.notebooks[0] + nb2 = repo2.notebooks[0] + return { + "UntitledNoDuplicates": nb1, + "dependenciesNotDeclared": nb2, + } + + @pytest.mark.parametrize( "test_input,expected", [ @@ -151,3 +165,15 @@ def test_long_filename(test_input, expected, notebooks): ) def test_invalid_python_syntax(test_input, expected, notebooks): assert nb_linting.invalid_python_syntax(notebooks[test_input]) == expected + + +@pytest.mark.parametrize( + "test_input, expected", + [("UntitledNoDuplicates", False), ("dependenciesNotDeclared", True)], +) +def test_undeclared_dependencies(test_input, expected, notebooks_from_repositories): + + assert ( + nb_linting.undeclared_dependencies(notebooks_from_repositories[test_input]) + == expected + ) diff --git a/tests/unit/test_repo_linting_functions.py b/tests/unit/test_repo_linting_functions.py index f882595..31e08ff 100644 --- a/tests/unit/test_repo_linting_functions.py +++ b/tests/unit/test_repo_linting_functions.py @@ -9,7 +9,6 @@ @pytest.fixture(scope="module") def repositories() -> Dict[str, Repository]: - repo_fixtures_base_path: Path = Path("tests/fixtures/test_repo") repo1 = Repository(repo_fixtures_base_path / "UntitledNoDuplicates") @@ -28,3 +27,11 @@ def test_coverage_data_not_available(test_input, expected, repositories): assert ( repo_linting.coverage_data_not_available(repositories[test_input]) == expected ) + + +@pytest.mark.parametrize( + "test_input, expected", + [("UntitledNoDuplicates", False), ("versioned_repo_with_coverage", True)], +) +def test_dependencies_unmanaged(test_input, expected, repositories): + assert repo_linting.dependencies_unmanaged(repositories[test_input]) == expected