diff --git a/.gitignore b/.gitignore index 5619d4b..1c180bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .coverage .idea .mypy_cache +.pytest_cache __pycache__ *.pyc @@ -8,3 +9,5 @@ __pycache__ dist output pysnyk.egg-info + +.venv \ No newline at end of file diff --git a/README.md b/README.md index e872d4a..2a0fafd 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ client = snyk.SnykClient("", tries=4, delay=1, backoff=2) - `tries` - the maximum number of attempts. **Default:** `1` (no retries) - `delay` - initial delay between attempts. **Default:** `1` - `backoff` - multiplier applied to delay between attempts. **Default:** `2` +- `debug` - run client in debug mode, useful for debugging API requests. **Default:** `False` ## Organizations @@ -60,21 +61,21 @@ Most of the API is scoped to organizations, so most other methods are found on t The `snyk.models.Organization` object has the following properties related to the API: -* `entitlements` - returns the set of Snyk features available to this account -* `dependencies`- returns a Manager for packages in use in this organization -* `licenses` - returns a Manager for licenses currently in use by projects in this organisation -* `members` - returns a Manager for members -* `projects` - returns a Manager for associated projects -* `integrations` - returns a Manager for active integrations +- `entitlements` - returns the set of Snyk features available to this account +- `dependencies`- returns a Manager for packages in use in this organization +- `licenses` - returns a Manager for licenses currently in use by projects in this organisation +- `members` - returns a Manager for members +- `projects` - returns a Manager for associated projects +- `integrations` - returns a Manager for active integrations ### A note on Managers Managers provide a consistent API for accessing objects from the Snyk API. Each manager implements the following methods: -* `all()` - return a list of all of the relevant objects -* `get("")` - return a single instance of the object if it exists -* `first()` - grab the first instance of the object if one exists -* `filter(="")` - return a list filtered by one or more key/value pairs +- `all()` - return a list of all of the relevant objects +- `get("")` - return a single instance of the object if it exists +- `first()` - grab the first instance of the object if one exists +- `filter(="")` - return a list filtered by one or more key/value pairs ### Projects @@ -94,20 +95,20 @@ client.projects.all() The `snyk.models.Project` object has the following useful properties and methods: -* `delete()` - deletes the project in question. Be careful as this will delete all associated data too -* `dependencies` - returns a Manager for packages in use in this project -* `dependency_graph` - returns a `snyk.models.DependencyGraph` object which represents the full dependency graph of package dependencies -* `ignores` - returns a Manager for ignore rules set on the project -* `vulnerabilities` - returns a list of `snyk.models.Vulnerability` objects with information about vulnerabilities in this project -* `jira_issues` - returns a Manager with access to any associated Jira issues -* `licenses` - returns a Manager for licenses currently in use by this project -* `settings` - returns a Manager for interacting with the current project settings -* `tags` - returns a Manager for interacting with the current project tags +- `delete()` - deletes the project in question. Be careful as this will delete all associated data too +- `dependencies` - returns a Manager for packages in use in this project +- `dependency_graph` - returns a `snyk.models.DependencyGraph` object which represents the full dependency graph of package dependencies +- `ignores` - returns a Manager for ignore rules set on the project +- `vulnerabilities` - returns a list of `snyk.models.Vulnerability` objects with information about vulnerabilities in this project +- `jira_issues` - returns a Manager with access to any associated Jira issues +- `licenses` - returns a Manager for licenses currently in use by this project +- `settings` - returns a Manager for interacting with the current project settings +- `tags` - returns a Manager for interacting with the current project tags You can add and delete tags using the manager: -* `tags.add(key, value)` - adds a tag with the provided key/value pair to the project -* `tags.delete(key, value)` - deletes a tag with the provided key/value pair from the project +- `tags.add(key, value)` - adds a tag with the provided key/value pair to the project +- `tags.delete(key, value)` - deletes a tag with the provided key/value pair from the project In the case of Projects, as well as filtering by properties (as mentioned above) you can also filter by tag: @@ -139,16 +140,14 @@ org.import_project("github.com/user/project@branch", files=["Gemfile.lock"]) This method currently only supports importing projects from GitHub and Docker Hub. For other integrations you will need to grab the lower-level `snyk.models.Integration` object from the `snyk.models.Organization.integrations` manager noted above. Other services will be added to this API soon. - ### Testing for vulnerabilities The API also exposes methods to discover vulnerability information about individual packages. These methods are found on the Organization object. -* `test_maven(, , )` - returns an IssueSet containing vulnerability information for a Maven artifact -* `test_rubygem(, )` - returns an IssueSet containing vulnerability information for a Ruby Gem -* `test_python(, )` - returns an IssueSet containing vulnerability information for Python package from PyPi -* `test_npm(, )` - returns an IssueSet containing vulnerability information for an NPM package - +- `test_maven(, , )` - returns an IssueSet containing vulnerability information for a Maven artifact +- `test_rubygem(, )` - returns an IssueSet containing vulnerability information for a Ruby Gem +- `test_python(, )` - returns an IssueSet containing vulnerability information for Python package from PyPi +- `test_npm(, )` - returns an IssueSet containing vulnerability information for an NPM package Here's an example of checking a particular Python package. @@ -166,14 +165,14 @@ False As well as testing individual packages you can also test all packages found in various dependency management manifests. The client currently supports the following methods: -* `test_pipfile()` - returns an IssueSet for all Python dependencies in a `Pipfile` -* `test_gemfilelock()` - returns an IssueSet for all Ruby dependencies in a `Gemfile` -* `test_packagejson(, ())` - returns an IssueSet for all Javascript dependencies in a `package.json` file. Optionally takes a `package.lock` file -* `test_gradlefile()` - returns an IssueSet for all dependencies in a `Gradlefile` -* `test_sbt()` - returns an IssueSet for all dependencies defined in a `.sbt` file -* `test_pom()` - returns an IssueSet for all dependencies in a Maven `pom.xml` file -* `test_yarn(, )` - returns an IssueSet for all dependencies in Yarn `package.json` and `yarn.lock` files -* `test_composer(, )` - returns an IssueSet for all dependencies in Composer `composer.json` and `composer.lock` files +- `test_pipfile()` - returns an IssueSet for all Python dependencies in a `Pipfile` +- `test_gemfilelock()` - returns an IssueSet for all Ruby dependencies in a `Gemfile` +- `test_packagejson(, ())` - returns an IssueSet for all Javascript dependencies in a `package.json` file. Optionally takes a `package.lock` file +- `test_gradlefile()` - returns an IssueSet for all dependencies in a `Gradlefile` +- `test_sbt()` - returns an IssueSet for all dependencies defined in a `.sbt` file +- `test_pom()` - returns an IssueSet for all dependencies in a Maven `pom.xml` file +- `test_yarn(, )` - returns an IssueSet for all dependencies in Yarn `package.json` and `yarn.lock` files +- `test_composer(, )` - returns an IssueSet for all dependencies in Composer `composer.json` and `composer.lock` files For example, here we are testing a Python `Pipfile`. @@ -199,7 +198,6 @@ You can also invite new users as administrators: >>> org.invite("example@example.com", admin=True) ``` - ### Low-level client As well as the high-level API of the Snyk client you can use the HTTP methods directly. For these you simply need to pass the path, and optionally a data payload. The full domain, and the authentication details, are already provided by the client. @@ -212,3 +210,60 @@ client.post("", ) ``` Most of the time you shouldn't need to use these. They are mainly useful if new methods are added to the API which are not yet supported in the client. This can also be useful if you want to pass very specific parameters, or to parse the raw JSON output from the API. + +## Experimental v3 low-level client + +pysnyk >= 0.9.0 now includes support for basic v3 compatibility. To switch to use a v3 client, pass the v3 API url and version when initializing a client. Right now it supports the `GET` method. Refer to the [v3 API docs](https://apidocs.snyk.io/) for more information and examples. + +Getting the v3 information of an organization: + +```python +# To get this value, get it from a Snyk organizations settings page +snyk_org = "df734bed-d75c-4f11-bb47-1d119913bcc7" + +# to use the v3 endpoint you MUST include a version value and the url of the v3 api endpoint as shown below +v3client = SnykClient(snyk_token, version="2022-02-16~experimental", url="https://api.snyk.io/v3") + +print(v3client.get(f"/orgs/{snyk_org}").json()) + +# this supports overriding v3 versions for a specific GET requests: +user = v3client.get(f"orgs/{snyk_org}/users/{snyk_user}", version="2022-02-01~experimental").json() + +# pass parameters such as how many results per page +params = {"limit": 10} + +targets = v3client.get(f"orgs/{snyk_org}/targets", params=params) +``` + +V1 and v3 can work at the same time by instantiating two clients: + +```python +snyk_org = "df734bed-d75c-4f11-bb47-1d119913bcc7" + +v1client = SnykClient(snyk_token) + +v3client = SnykClient(snyk_token, version="2022-02-16~experimental", url="https://api.snyk.io/v3") + +v1_org = v1client.organizations.get(snyk_org) + +v3_org = v3client.get(f"/orgs/{snyk_org}").json() +``` + +The v3 API introduces consistent pagination across all endpoints. The v3 client includes a helper method `.get_v3_pages` which collects the paginated responses and returns a single list combining the contents of the "data" key from all pages. It takes the same values as the get method. + +```python +v3client = SnykClient(snyk_token, version="2022-02-16~experimental", url="https://api.snyk.io/v3") + +params = {"limit": 10} + +targets = v3client.get(f"orgs/{snyk_org}/targets", params=params).json() + +print(len(targets["data"])) +# returns 10 targets + +all_targets = v3client.get_v3_pages(f"orgs/{snyk_org}/targets", params=params) + +print(len(all_targets)) +# returns 33 targets, note we don't have to add .json() to the call or access the "data" key, get_v3_pages does that for us + +``` diff --git a/poetry.lock b/poetry.lock index ceadd78..db7937b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,11 +1,3 @@ -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "atomicwrites" version = "1.4.0" @@ -30,23 +22,26 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "black" -version = "19.10b0" +version = "22.1.0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] -appdirs = "*" -attrs = ">=18.1.0" -click = ">=6.5" -pathspec = ">=0.6,<1" -regex = "*" -toml = ">=0.9.4" -typed-ast = ">=1.4.0" +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = ">=1.1.0" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} [package.extras] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" @@ -69,7 +64,7 @@ unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.3" +version = "8.0.4" description = "Composable command line interface toolkit" category = "dev" optional = false @@ -89,7 +84,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "coverage" -version = "6.3.1" +version = "6.3.2" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -122,7 +117,7 @@ packaging = "*" [[package]] name = "filelock" -version = "3.5.1" +version = "3.6.0" description = "A platform independent file lock." category = "dev" optional = false @@ -142,7 +137,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.11.1" +version = "4.11.2" description = "Read metadata from Python packages" category = "main" optional = false @@ -153,7 +148,7 @@ typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""} zipp = ">=0.5" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)"] perf = ["ipython"] testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] @@ -239,6 +234,18 @@ category = "dev" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +[[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" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + [[package]] name = "pluggy" version = "1.0.0" @@ -325,17 +332,16 @@ testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtuale [[package]] name = "pytest-isort" -version = "1.3.0" +version = "3.0.0" description = "py.test plugin to check import ordering using isort" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6,<4" [package.dependencies] +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} isort = ">=4.0" - -[package.extras] -tests = ["mock"] +pytest = ">=5.0" [[package]] name = "pytest-mypy" @@ -358,14 +364,6 @@ pytest = [ {version = ">=4.6", markers = "python_version >= \"3.6\" and python_version < \"3.10\""}, ] -[[package]] -name = "regex" -version = "2022.1.18" -description = "Alternative regular expression module, to replace re." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "requests" version = "2.27.1" @@ -444,9 +442,17 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "types-backports" +version = "0.1.3" +description = "Typing stubs for backports" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "types-requests" -version = "2.27.10" +version = "2.27.11" description = "Typing stubs for requests" category = "dev" optional = false @@ -455,9 +461,33 @@ python-versions = "*" [package.dependencies] types-urllib3 = "<1.27" +[[package]] +name = "types-retry" +version = "0.9.5" +description = "Typing stubs for retry" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-setuptools" +version = "57.4.9" +description = "Typing stubs for setuptools" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-toml" +version = "0.10.4" +description = "Typing stubs for toml" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "types-urllib3" -version = "1.26.9" +version = "1.26.10" description = "Typing stubs for urllib3" category = "dev" optional = false @@ -486,11 +516,11 @@ socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "xlsxwriter" -version = "1.4.5" +version = "3.0.3" description = "A Python module for creating Excel XLSX files." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.4" [[package]] name = "zipp" @@ -507,13 +537,9 @@ testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest- [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "af5fbbbd55df71d2f5ffb2a4ecef47a234275734e1a2e48099493ac4804c818f" +content-hash = "e1ed0f4324c29fa3943fdc8b035d4c4aa9858f1d173b4fb2943ba8865d81be1c" [metadata.files] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] atomicwrites = [ {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, @@ -523,8 +549,29 @@ attrs = [ {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, ] black = [ - {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"}, - {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"}, + {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, + {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, + {file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"}, + {file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"}, + {file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"}, + {file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"}, + {file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"}, + {file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"}, + {file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"}, + {file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"}, + {file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"}, + {file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"}, + {file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"}, + {file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"}, + {file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"}, + {file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"}, + {file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"}, + {file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"}, + {file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"}, + {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, + {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, ] certifi = [ {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, @@ -535,55 +582,55 @@ charset-normalizer = [ {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, ] click = [ - {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, - {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, + {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, + {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, ] colorama = [ {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, ] coverage = [ - {file = "coverage-6.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:eeffd96882d8c06d31b65dddcf51db7c612547babc1c4c5db6a011abe9798525"}, - {file = "coverage-6.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:621f6ea7260ea2ffdaec64fe5cb521669984f567b66f62f81445221d4754df4c"}, - {file = "coverage-6.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84f2436d6742c01136dd940ee158bfc7cf5ced3da7e4c949662b8703b5cd8145"}, - {file = "coverage-6.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de73fca6fb403dd72d4da517cfc49fcf791f74eee697d3219f6be29adf5af6ce"}, - {file = "coverage-6.3.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78fbb2be068a13a5d99dce9e1e7d168db880870f7bc73f876152130575bd6167"}, - {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f5a4551dfd09c3bd12fca8144d47fe7745275adf3229b7223c2f9e29a975ebda"}, - {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7bff3a98f63b47464480de1b5bdd80c8fade0ba2832c9381253c9b74c4153c27"}, - {file = "coverage-6.3.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a06c358f4aed05fa1099c39decc8022261bb07dfadc127c08cfbd1391b09689e"}, - {file = "coverage-6.3.1-cp310-cp310-win32.whl", hash = "sha256:9fff3ff052922cb99f9e52f63f985d4f7a54f6b94287463bc66b7cdf3eb41217"}, - {file = "coverage-6.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:276b13cc085474e482566c477c25ed66a097b44c6e77132f3304ac0b039f83eb"}, - {file = "coverage-6.3.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:56c4a409381ddd7bbff134e9756077860d4e8a583d310a6f38a2315b9ce301d0"}, - {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eb494070aa060ceba6e4bbf44c1bc5fa97bfb883a0d9b0c9049415f9e944793"}, - {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5e15d424b8153756b7c903bde6d4610be0c3daca3986173c18dd5c1a1625e4cd"}, - {file = "coverage-6.3.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:61d47a897c1e91f33f177c21de897267b38fbb45f2cd8e22a710bcef1df09ac1"}, - {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:25e73d4c81efa8ea3785274a2f7f3bfbbeccb6fcba2a0bdd3be9223371c37554"}, - {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:fac0bcc5b7e8169bffa87f0dcc24435446d329cbc2b5486d155c2e0f3b493ae1"}, - {file = "coverage-6.3.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:72128176fea72012063200b7b395ed8a57849282b207321124d7ff14e26988e8"}, - {file = "coverage-6.3.1-cp37-cp37m-win32.whl", hash = "sha256:1bc6d709939ff262fd1432f03f080c5042dc6508b6e0d3d20e61dd045456a1a0"}, - {file = "coverage-6.3.1-cp37-cp37m-win_amd64.whl", hash = "sha256:618eeba986cea7f621d8607ee378ecc8c2504b98b3fdc4952b30fe3578304687"}, - {file = "coverage-6.3.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d5ed164af5c9078596cfc40b078c3b337911190d3faeac830c3f1274f26b8320"}, - {file = "coverage-6.3.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:352c68e233409c31048a3725c446a9e48bbff36e39db92774d4f2380d630d8f8"}, - {file = "coverage-6.3.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:448d7bde7ceb6c69e08474c2ddbc5b4cd13c9e4aa4a717467f716b5fc938a734"}, - {file = "coverage-6.3.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9fde6b90889522c220dd56a670102ceef24955d994ff7af2cb786b4ba8fe11e4"}, - {file = "coverage-6.3.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e647a0be741edbb529a72644e999acb09f2ad60465f80757da183528941ff975"}, - {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a5cdc3adb4f8bb8d8f5e64c2e9e282bc12980ef055ec6da59db562ee9bdfefa"}, - {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2dd70a167843b4b4b2630c0c56f1b586fe965b4f8ac5da05b6690344fd065c6b"}, - {file = "coverage-6.3.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9ad0a117b8dc2061ce9461ea4c1b4799e55edceb236522c5b8f958ce9ed8fa9a"}, - {file = "coverage-6.3.1-cp38-cp38-win32.whl", hash = "sha256:e92c7a5f7d62edff50f60a045dc9542bf939758c95b2fcd686175dd10ce0ed10"}, - {file = "coverage-6.3.1-cp38-cp38-win_amd64.whl", hash = "sha256:482fb42eea6164894ff82abbcf33d526362de5d1a7ed25af7ecbdddd28fc124f"}, - {file = "coverage-6.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c5b81fb37db76ebea79aa963b76d96ff854e7662921ce742293463635a87a78d"}, - {file = "coverage-6.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a4f923b9ab265136e57cc14794a15b9dcea07a9c578609cd5dbbfff28a0d15e6"}, - {file = "coverage-6.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56d296cbc8254a7dffdd7bcc2eb70be5a233aae7c01856d2d936f5ac4e8ac1f1"}, - {file = "coverage-6.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1245ab82e8554fa88c4b2ab1e098ae051faac5af829efdcf2ce6b34dccd5567c"}, - {file = "coverage-6.3.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f2b05757c92ad96b33dbf8e8ec8d4ccb9af6ae3c9e9bd141c7cc44d20c6bcba"}, - {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9e3dd806f34de38d4c01416344e98eab2437ac450b3ae39c62a0ede2f8b5e4ed"}, - {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:d651fde74a4d3122e5562705824507e2f5b2d3d57557f1916c4b27635f8fbe3f"}, - {file = "coverage-6.3.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:704f89b87c4f4737da2860695a18c852b78ec7279b24eedacab10b29067d3a38"}, - {file = "coverage-6.3.1-cp39-cp39-win32.whl", hash = "sha256:2aed4761809640f02e44e16b8b32c1a5dee5e80ea30a0ff0912158bde9c501f2"}, - {file = "coverage-6.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:9976fb0a5709988778ac9bc44f3d50fccd989987876dfd7716dee28beed0a9fa"}, - {file = "coverage-6.3.1-pp36.pp37.pp38-none-any.whl", hash = "sha256:463e52616ea687fd323888e86bf25e864a3cc6335a043fad6bbb037dbf49bbe2"}, - {file = "coverage-6.3.1.tar.gz", hash = "sha256:6c3f6158b02ac403868eea390930ae64e9a9a2a5bbfafefbb920d29258d9f2f8"}, + {file = "coverage-6.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9b27d894748475fa858f9597c0ee1d4829f44683f3813633aaf94b19cb5453cf"}, + {file = "coverage-6.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37d1141ad6b2466a7b53a22e08fe76994c2d35a5b6b469590424a9953155afac"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9987b0354b06d4df0f4d3e0ec1ae76d7ce7cbca9a2f98c25041eb79eec766f1"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26e2deacd414fc2f97dd9f7676ee3eaecd299ca751412d89f40bc01557a6b1b4"}, + {file = "coverage-6.3.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4dd8bafa458b5c7d061540f1ee9f18025a68e2d8471b3e858a9dad47c8d41903"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:46191097ebc381fbf89bdce207a6c107ac4ec0890d8d20f3360345ff5976155c"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6f89d05e028d274ce4fa1a86887b071ae1755082ef94a6740238cd7a8178804f"}, + {file = "coverage-6.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:58303469e9a272b4abdb9e302a780072c0633cdcc0165db7eec0f9e32f901e05"}, + {file = "coverage-6.3.2-cp310-cp310-win32.whl", hash = "sha256:2fea046bfb455510e05be95e879f0e768d45c10c11509e20e06d8fcaa31d9e39"}, + {file = "coverage-6.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:a2a8b8bcc399edb4347a5ca8b9b87e7524c0967b335fbb08a83c8421489ddee1"}, + {file = "coverage-6.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f1555ea6d6da108e1999b2463ea1003fe03f29213e459145e70edbaf3e004aaa"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5f4e1edcf57ce94e5475fe09e5afa3e3145081318e5fd1a43a6b4539a97e518"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7a15dc0a14008f1da3d1ebd44bdda3e357dbabdf5a0b5034d38fcde0b5c234b7"}, + {file = "coverage-6.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21b7745788866028adeb1e0eca3bf1101109e2dc58456cb49d2d9b99a8c516e6"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:8ce257cac556cb03be4a248d92ed36904a59a4a5ff55a994e92214cde15c5bad"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b0be84e5a6209858a1d3e8d1806c46214e867ce1b0fd32e4ea03f4bd8b2e3359"}, + {file = "coverage-6.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:acf53bc2cf7282ab9b8ba346746afe703474004d9e566ad164c91a7a59f188a4"}, + {file = "coverage-6.3.2-cp37-cp37m-win32.whl", hash = "sha256:8bdde1177f2311ee552f47ae6e5aa7750c0e3291ca6b75f71f7ffe1f1dab3dca"}, + {file = "coverage-6.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b31651d018b23ec463e95cf10070d0b2c548aa950a03d0b559eaa11c7e5a6fa3"}, + {file = "coverage-6.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:07e6db90cd9686c767dcc593dff16c8c09f9814f5e9c51034066cad3373b914d"}, + {file = "coverage-6.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c6dbb42f3ad25760010c45191e9757e7dce981cbfb90e42feef301d71540059"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76aeef1b95aff3905fb2ae2d96e319caca5b76fa41d3470b19d4e4a3a313512"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cf5cfcb1521dc3255d845d9dca3ff204b3229401994ef8d1984b32746bb45ca"}, + {file = "coverage-6.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8fbbdc8d55990eac1b0919ca69eb5a988a802b854488c34b8f37f3e2025fa90d"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ec6bc7fe73a938933d4178c9b23c4e0568e43e220aef9472c4f6044bfc6dd0f0"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9baff2a45ae1f17c8078452e9e5962e518eab705e50a0aa8083733ea7d45f3a6"}, + {file = "coverage-6.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd9e830e9d8d89b20ab1e5af09b32d33e1a08ef4c4e14411e559556fd788e6b2"}, + {file = "coverage-6.3.2-cp38-cp38-win32.whl", hash = "sha256:f7331dbf301b7289013175087636bbaf5b2405e57259dd2c42fdcc9fcc47325e"}, + {file = "coverage-6.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:68353fe7cdf91f109fc7d474461b46e7f1f14e533e911a2a2cbb8b0fc8613cf1"}, + {file = "coverage-6.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b78e5afb39941572209f71866aa0b206c12f0109835aa0d601e41552f9b3e620"}, + {file = "coverage-6.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4e21876082ed887baed0146fe222f861b5815455ada3b33b890f4105d806128d"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34626a7eee2a3da12af0507780bb51eb52dca0e1751fd1471d0810539cefb536"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1ebf730d2381158ecf3dfd4453fbca0613e16eaa547b4170e2450c9707665ce7"}, + {file = "coverage-6.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd6fe30bd519694b356cbfcaca9bd5c1737cddd20778c6a581ae20dc8c04def2"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:96f8a1cb43ca1422f36492bebe63312d396491a9165ed3b9231e778d43a7fca4"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dd035edafefee4d573140a76fdc785dc38829fe5a455c4bb12bac8c20cfc3d69"}, + {file = "coverage-6.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ca5aeb4344b30d0bec47481536b8ba1181d50dbe783b0e4ad03c95dc1296684"}, + {file = "coverage-6.3.2-cp39-cp39-win32.whl", hash = "sha256:f5fa5803f47e095d7ad8443d28b01d48c0359484fec1b9d8606d0e3282084bc4"}, + {file = "coverage-6.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:9548f10d8be799551eb3a9c74bbf2b4934ddb330e08a73320123c07f95cc2d92"}, + {file = "coverage-6.3.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:18d520c6860515a771708937d2f78f63cc47ab3b80cb78e86573b0a760161faf"}, + {file = "coverage-6.3.2.tar.gz", hash = "sha256:03e2a7826086b91ef345ff18742ee9fc47a6839ccd517061ef8fa1976e652ce9"}, ] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, @@ -594,16 +641,16 @@ deprecation = [ {file = "deprecation-2.1.0.tar.gz", hash = "sha256:72b3bde64e5d778694b0cf68178aed03d15e15477116add3fb773e581f9518ff"}, ] filelock = [ - {file = "filelock-3.5.1-py3-none-any.whl", hash = "sha256:7b23620a293cf3e19924e469cb96672dc72b36c26e8f80f85668310117fcbe4e"}, - {file = "filelock-3.5.1.tar.gz", hash = "sha256:d1eccb164ed020bc84edd9e45bf6cdb177f64749f6b8fe066648832d2e98726d"}, + {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, + {file = "filelock-3.6.0.tar.gz", hash = "sha256:9cd540a9352e432c7246a48fe4e8712b10acb1df2ad1f30e8c070b82ae1fed85"}, ] idna = [ {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-4.11.1-py3-none-any.whl", hash = "sha256:e0bc84ff355328a4adfc5240c4f211e0ab386f80aa640d1b11f0618a1d282094"}, - {file = "importlib_metadata-4.11.1.tar.gz", hash = "sha256:175f4ee440a0317f6e8d81b7f8d4869f93316170a65ad2b007d2929186c8052c"}, + {file = "importlib_metadata-4.11.2-py3-none-any.whl", hash = "sha256:d16e8c1deb60de41b8e8ed21c1a7b947b0bc62fab7e1d470bcdf331cea2e6735"}, + {file = "importlib_metadata-4.11.2.tar.gz", hash = "sha256:b36ffa925fe3139b2f6ff11d6925ffd4fa7bc47870165e3ac260ac7b4f91e6ac"}, ] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, @@ -651,6 +698,10 @@ pathspec = [ {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] +platformdirs = [ + {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"}, + {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"}, +] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, @@ -675,89 +726,13 @@ pytest-cov = [ {file = "pytest_cov-3.0.0-py3-none-any.whl", hash = "sha256:578d5d15ac4a25e5f961c938b85a05b09fdaae9deef3bb6de9a6e766622ca7a6"}, ] pytest-isort = [ - {file = "pytest-isort-1.3.0.tar.gz", hash = "sha256:46a12331a701e2f21d48548b2828c8b0a7956dbf1cd5347163f537deb24332dd"}, - {file = "pytest_isort-1.3.0-py3-none-any.whl", hash = "sha256:074255ad393088a2daee6ca7f2305b7b86358ff632f62302896d8d4b2b339107"}, + {file = "pytest-isort-3.0.0.tar.gz", hash = "sha256:4fe4b26ead2af776730ec23f5870d7421f35aace22a41c4e938586ef4d8787cb"}, + {file = "pytest_isort-3.0.0-py3-none-any.whl", hash = "sha256:2d96a25a135d6fd084ac36878e7d54f26f27c6987c2c65f0d12809bffade9cb9"}, ] pytest-mypy = [ {file = "pytest-mypy-0.9.1.tar.gz", hash = "sha256:9ffa3bf405c12c5c6be9e92e22bebb6ab2c91b9c32f45b0f0c93af473269ab5c"}, {file = "pytest_mypy-0.9.1-py3-none-any.whl", hash = "sha256:a2505fcf61f1c0c51f950d4623ea8ca2daf6fb2101a5603554bad2e130202083"}, ] -regex = [ - {file = "regex-2022.1.18-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:34316bf693b1d2d29c087ee7e4bb10cdfa39da5f9c50fa15b07489b4ab93a1b5"}, - {file = "regex-2022.1.18-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7a0b9f6a1a15d494b35f25ed07abda03209fa76c33564c09c9e81d34f4b919d7"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f99112aed4fb7cee00c7f77e8b964a9b10f69488cdff626ffd797d02e2e4484f"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a2bf98ac92f58777c0fafc772bf0493e67fcf677302e0c0a630ee517a43b949"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8618d9213a863c468a865e9d2ec50221015f7abf52221bc927152ef26c484b4c"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b52cc45e71657bc4743a5606d9023459de929b2a198d545868e11898ba1c3f59"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e12949e5071c20ec49ef00c75121ed2b076972132fc1913ddf5f76cae8d10b4"}, - {file = "regex-2022.1.18-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b02e3e72665cd02afafb933453b0c9f6c59ff6e3708bd28d0d8580450e7e88af"}, - {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:abfcb0ef78df0ee9df4ea81f03beea41849340ce33a4c4bd4dbb99e23ec781b6"}, - {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6213713ac743b190ecbf3f316d6e41d099e774812d470422b3a0f137ea635832"}, - {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:61ebbcd208d78658b09e19c78920f1ad38936a0aa0f9c459c46c197d11c580a0"}, - {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:b013f759cd69cb0a62de954d6d2096d648bc210034b79b1881406b07ed0a83f9"}, - {file = "regex-2022.1.18-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9187500d83fd0cef4669385cbb0961e227a41c0c9bc39219044e35810793edf7"}, - {file = "regex-2022.1.18-cp310-cp310-win32.whl", hash = "sha256:94c623c331a48a5ccc7d25271399aff29729fa202c737ae3b4b28b89d2b0976d"}, - {file = "regex-2022.1.18-cp310-cp310-win_amd64.whl", hash = "sha256:1a171eaac36a08964d023eeff740b18a415f79aeb212169080c170ec42dd5184"}, - {file = "regex-2022.1.18-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:49810f907dfe6de8da5da7d2b238d343e6add62f01a15d03e2195afc180059ed"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d2f5c3f7057530afd7b739ed42eb04f1011203bc5e4663e1e1d01bb50f813e3"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85ffd6b1cb0dfb037ede50ff3bef80d9bf7fa60515d192403af6745524524f3b"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ba37f11e1d020969e8a779c06b4af866ffb6b854d7229db63c5fdddfceaa917f"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637e27ea1ebe4a561db75a880ac659ff439dec7f55588212e71700bb1ddd5af9"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37978254d9d00cda01acc1997513f786b6b971e57b778fbe7c20e30ae81a97f3"}, - {file = "regex-2022.1.18-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e54a1eb9fd38f2779e973d2f8958fd575b532fe26013405d1afb9ee2374e7ab8"}, - {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:768632fd8172ae03852e3245f11c8a425d95f65ff444ce46b3e673ae5b057b74"}, - {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:de2923886b5d3214be951bc2ce3f6b8ac0d6dfd4a0d0e2a4d2e5523d8046fdfb"}, - {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:1333b3ce73269f986b1fa4d5d395643810074dc2de5b9d262eb258daf37dc98f"}, - {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:d19a34f8a3429bd536996ad53597b805c10352a8561d8382e05830df389d2b43"}, - {file = "regex-2022.1.18-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:8d2f355a951f60f0843f2368b39970e4667517e54e86b1508e76f92b44811a8a"}, - {file = "regex-2022.1.18-cp36-cp36m-win32.whl", hash = "sha256:2245441445099411b528379dee83e56eadf449db924648e5feb9b747473f42e3"}, - {file = "regex-2022.1.18-cp36-cp36m-win_amd64.whl", hash = "sha256:25716aa70a0d153cd844fe861d4f3315a6ccafce22b39d8aadbf7fcadff2b633"}, - {file = "regex-2022.1.18-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7e070d3aef50ac3856f2ef5ec7214798453da878bb5e5a16c16a61edf1817cc3"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22709d701e7037e64dae2a04855021b62efd64a66c3ceed99dfd684bfef09e38"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9099bf89078675c372339011ccfc9ec310310bf6c292b413c013eb90ffdcafc"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04611cc0f627fc4a50bc4a9a2e6178a974c6a6a4aa9c1cca921635d2c47b9c87"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:552a39987ac6655dad4bf6f17dd2b55c7b0c6e949d933b8846d2e312ee80005a"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e031899cb2bc92c0cf4d45389eff5b078d1936860a1be3aa8c94fa25fb46ed8"}, - {file = "regex-2022.1.18-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2dacb3dae6b8cc579637a7b72f008bff50a94cde5e36e432352f4ca57b9e54c4"}, - {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e5c31d70a478b0ca22a9d2d76d520ae996214019d39ed7dd93af872c7f301e52"}, - {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bb804c7d0bfbd7e3f33924ff49757de9106c44e27979e2492819c16972ec0da2"}, - {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:36b2d700a27e168fa96272b42d28c7ac3ff72030c67b32f37c05616ebd22a202"}, - {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:16f81025bb3556eccb0681d7946e2b35ff254f9f888cff7d2120e8826330315c"}, - {file = "regex-2022.1.18-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:da80047524eac2acf7c04c18ac7a7da05a9136241f642dd2ed94269ef0d0a45a"}, - {file = "regex-2022.1.18-cp37-cp37m-win32.whl", hash = "sha256:6ca45359d7a21644793de0e29de497ef7f1ae7268e346c4faf87b421fea364e6"}, - {file = "regex-2022.1.18-cp37-cp37m-win_amd64.whl", hash = "sha256:38289f1690a7e27aacd049e420769b996826f3728756859420eeee21cc857118"}, - {file = "regex-2022.1.18-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6014038f52b4b2ac1fa41a58d439a8a00f015b5c0735a0cd4b09afe344c94899"}, - {file = "regex-2022.1.18-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b5d6f9aed3153487252d00a18e53f19b7f52a1651bc1d0c4b5844bc286dfa52"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9d24b03daf7415f78abc2d25a208f234e2c585e5e6f92f0204d2ab7b9ab48e3"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bf594cc7cc9d528338d66674c10a5b25e3cde7dd75c3e96784df8f371d77a298"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd914db437ec25bfa410f8aa0aa2f3ba87cdfc04d9919d608d02330947afaeab"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b6840b6448203228a9d8464a7a0d99aa8fa9f027ef95fe230579abaf8a6ee1"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11772be1eb1748e0e197a40ffb82fb8fd0d6914cd147d841d9703e2bef24d288"}, - {file = "regex-2022.1.18-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a602bdc8607c99eb5b391592d58c92618dcd1537fdd87df1813f03fed49957a6"}, - {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7e26eac9e52e8ce86f915fd33380f1b6896a2b51994e40bb094841e5003429b4"}, - {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:519c0b3a6fbb68afaa0febf0d28f6c4b0a1074aefc484802ecb9709faf181607"}, - {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3c7ea86b9ca83e30fa4d4cd0eaf01db3ebcc7b2726a25990966627e39577d729"}, - {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:51f02ca184518702975b56affde6c573ebad4e411599005ce4468b1014b4786c"}, - {file = "regex-2022.1.18-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:385ccf6d011b97768a640e9d4de25412204fbe8d6b9ae39ff115d4ff03f6fe5d"}, - {file = "regex-2022.1.18-cp38-cp38-win32.whl", hash = "sha256:1f8c0ae0a0de4e19fddaaff036f508db175f6f03db318c80bbc239a1def62d02"}, - {file = "regex-2022.1.18-cp38-cp38-win_amd64.whl", hash = "sha256:760c54ad1b8a9b81951030a7e8e7c3ec0964c1cb9fee585a03ff53d9e531bb8e"}, - {file = "regex-2022.1.18-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:93c20777a72cae8620203ac11c4010365706062aa13aaedd1a21bb07adbb9d5d"}, - {file = "regex-2022.1.18-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6aa427c55a0abec450bca10b64446331b5ca8f79b648531138f357569705bc4a"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c38baee6bdb7fe1b110b6b3aaa555e6e872d322206b7245aa39572d3fc991ee4"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:752e7ddfb743344d447367baa85bccd3629c2c3940f70506eb5f01abce98ee68"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8acef4d8a4353f6678fd1035422a937c2170de58a2b29f7da045d5249e934101"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c73d2166e4b210b73d1429c4f1ca97cea9cc090e5302df2a7a0a96ce55373f1c"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:24c89346734a4e4d60ecf9b27cac4c1fee3431a413f7aa00be7c4d7bbacc2c4d"}, - {file = "regex-2022.1.18-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:596f5ae2eeddb79b595583c2e0285312b2783b0ec759930c272dbf02f851ff75"}, - {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ecfe51abf7f045e0b9cdde71ca9e153d11238679ef7b5da6c82093874adf3338"}, - {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:1d6301f5288e9bdca65fab3de6b7de17362c5016d6bf8ee4ba4cbe833b2eda0f"}, - {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:93cce7d422a0093cfb3606beae38a8e47a25232eea0f292c878af580a9dc7605"}, - {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cf0db26a1f76aa6b3aa314a74b8facd586b7a5457d05b64f8082a62c9c49582a"}, - {file = "regex-2022.1.18-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:defa0652696ff0ba48c8aff5a1fac1eef1ca6ac9c660b047fc8e7623c4eb5093"}, - {file = "regex-2022.1.18-cp39-cp39-win32.whl", hash = "sha256:6db1b52c6f2c04fafc8da17ea506608e6be7086715dab498570c3e55e4f8fbd1"}, - {file = "regex-2022.1.18-cp39-cp39-win_amd64.whl", hash = "sha256:ebaeb93f90c0903233b11ce913a7cb8f6ee069158406e056f884854c737d2442"}, - {file = "regex-2022.1.18.tar.gz", hash = "sha256:97f32dc03a8054a4c4a5ab5d761ed4861e828b2c200febd4e46857069a483916"}, -] requests = [ {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, @@ -808,13 +783,29 @@ typed-ast = [ {file = "typed_ast-1.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:df05aa5b241e2e8045f5f4367a9f6187b09c4cdf8578bb219861c4e27c443db5"}, {file = "typed_ast-1.5.2.tar.gz", hash = "sha256:525a2d4088e70a9f75b08b3f87a51acc9cde640e19cc523c7e41aa355564ae27"}, ] +types-backports = [ + {file = "types-backports-0.1.3.tar.gz", hash = "sha256:f4b7206c073df88d6200891e3d27506185fd60cda66fb289737b2fa92c0010cf"}, + {file = "types_backports-0.1.3-py2.py3-none-any.whl", hash = "sha256:dafcd61848081503e738a7768872d1dd6c018401b4d2a1cfb608ea87ec9864b9"}, +] types-requests = [ - {file = "types-requests-2.27.10.tar.gz", hash = "sha256:5dcb088fcaa778efeee6b7fc46967037e983fbfb9fec02594578bd33fd75e555"}, - {file = "types_requests-2.27.10-py3-none-any.whl", hash = "sha256:6cb4fb0bbcbc585c57eeee6ffe5a47638dc89706b8d290ec89a77213fc5bad1a"}, + {file = "types-requests-2.27.11.tar.gz", hash = "sha256:6a7ed24b21780af4a5b5e24c310b2cd885fb612df5fd95584d03d87e5f2a195a"}, + {file = "types_requests-2.27.11-py3-none-any.whl", hash = "sha256:506279bad570c7b4b19ac1f22e50146538befbe0c133b2cea66a9b04a533a859"}, +] +types-retry = [ + {file = "types-retry-0.9.5.tar.gz", hash = "sha256:35a2ff8b987258154fb9c302a39df9e8691041b785f34a444a8d47660ef9b4cd"}, + {file = "types_retry-0.9.5-py3-none-any.whl", hash = "sha256:591393de4b821e6609914c62e64e4334c1ad7707d84ec9ee47ceae901ce6c23f"}, +] +types-setuptools = [ + {file = "types-setuptools-57.4.9.tar.gz", hash = "sha256:536ef74744f8e1e4be4fc719887f886e74e4cf3c792b4a06984320be4df450b5"}, + {file = "types_setuptools-57.4.9-py3-none-any.whl", hash = "sha256:948dc6863373750e2cd0b223a84f1fb608414cde5e55cf38ea657b93aeb411d2"}, +] +types-toml = [ + {file = "types-toml-0.10.4.tar.gz", hash = "sha256:9340e7c1587715581bb13905b3af30b79fe68afaccfca377665d5e63b694129a"}, + {file = "types_toml-0.10.4-py3-none-any.whl", hash = "sha256:4a9ffd47bbcec49c6fde6351a889b2c1bd3c0ef309fa0eed60dc28e58c8b9ea6"}, ] types-urllib3 = [ - {file = "types-urllib3-1.26.9.tar.gz", hash = "sha256:abd2d4857837482b1834b4817f0587678dcc531dbc9abe4cde4da28cef3f522c"}, - {file = "types_urllib3-1.26.9-py3-none-any.whl", hash = "sha256:4a54f6274ab1c80968115634a55fb9341a699492b95e32104a7c513db9fe02e9"}, + {file = "types-urllib3-1.26.10.tar.gz", hash = "sha256:a26898f530e6c3f43f25b907f2b884486868ffd56a9faa94cbf9b3eb6e165d6a"}, + {file = "types_urllib3-1.26.10-py3-none-any.whl", hash = "sha256:d755278d5ecd7a7a6479a190e54230f241f1a99c19b81518b756b19dc69e518c"}, ] typing-extensions = [ {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, @@ -825,8 +816,8 @@ urllib3 = [ {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, ] xlsxwriter = [ - {file = "XlsxWriter-1.4.5-py2.py3-none-any.whl", hash = "sha256:f9335f1736e2c4fd80e940fe1b6d92d967bf454a1e5d639b0b7a4459ade790cc"}, - {file = "XlsxWriter-1.4.5.tar.gz", hash = "sha256:0956747859567ec01907e561a7d8413de18a7aae36860f979f9da52b9d58bc19"}, + {file = "XlsxWriter-3.0.3-py3-none-any.whl", hash = "sha256:df0aefe5137478d206847eccf9f114715e42aaea077e6a48d0e8a2152e983010"}, + {file = "XlsxWriter-3.0.3.tar.gz", hash = "sha256:e89f4a1d2fa2c9ea15cde77de95cd3fd8b0345d0efb3964623f395c8c4988b7f"}, ] zipp = [ {file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"}, diff --git a/pyproject.toml b/pyproject.toml index 943aa30..71b0d18 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "pysnyk" -version = "0.8.1" +version = "0.9.0" description = "A Python client for the Snyk API" authors = [ "Gareth Rushgrove ", @@ -14,29 +14,31 @@ repository = "https://github.com/snyk-labs/pysnyk" [tool.poetry.dependencies] python = "^3.7" -requests = "^2.22" +requests = "^2.27.1" mashumaro = "^3" -importlib_metadata = "^4.4" +importlib_metadata = "^4.11.2" retry = "^0.9.2" deprecation = "^2.1.0" [tool.poetry.dev-dependencies] pytest = "^6.2.5" -pytest-black = "^0.3.10" +pytest-black = "^0.3.12" pytest-cov = "^3.0.0" pytest-mypy = "^0.9.1" -requests-mock = "^1.7" -xlsxwriter = "^1.1.8" -pytest-isort = "^1.1.0" -black = "^19.10b0" -types-requests = "^2.27.10" +requests-mock = "^1.9.3" +xlsxwriter = "^3.0.3" +pytest-isort = "^3" +black = "^22.1" +types-requests = "^2.27.11" +coverage = "^6.3.2" +types-backports = "^0.1.3" +types-retry = "^0.9.5" +types-setuptools = "^57.4.9" +types-toml = "^0.10.4" [tool.isort] -multi_line_output = 3 -include_trailing_comma = true -force_grid_wrap = 0 -combine_as_imports = true -line_length = 88 +profile = "black" + [build-system] requires = ["poetry>=0.12"] build-backend = "poetry.masonry.api" diff --git a/snyk/client.py b/snyk/client.py index 389c09b..91feed4 100644 --- a/snyk/client.py +++ b/snyk/client.py @@ -1,13 +1,15 @@ import logging -from typing import Any, Optional +import urllib.parse +from typing import Any, List, Optional import requests -from retry.api import retry_call # type: ignore +from retry.api import retry_call from .__version__ import __version__ from .errors import SnykHTTPError, SnykNotImplementedError from .managers import Manager from .models import Organization, Project +from .utils import cleanup_path logger = logging.getLogger(__name__) @@ -25,6 +27,7 @@ def __init__( tries: int = 1, delay: int = 1, backoff: int = 2, + version: Optional[str] = None, ): self.api_token = token self.api_url = url or self.API_URL @@ -37,16 +40,40 @@ def __init__( self.tries = tries self.backoff = backoff self.delay = delay + self.version = version + + # Ensure we don't have a trailing / + if self.api_url[-1] == "/": + self.api_url = self.api_url.rstrip("/") + + if debug: + logging.basicConfig(level=logging.DEBUG) + + def request( + self, + method, + url: str, + headers: object, + params: object = None, + json: object = None, + ) -> requests.Response: + + if params and json: + resp = method(url, headers=headers, params=params, json=json) + elif params and not json: + resp = method(url, headers=headers, params=params) + elif json and not params: + resp = method(url, headers=headers, json=json) + else: + resp = method(url, headers=headers) - def request(self, method, url: str, headers: object, json={},) -> requests.Response: - resp = method(url, json=json, headers=headers,) if not resp or resp.status_code >= requests.codes.server_error: raise SnykHTTPError(resp) return resp def post(self, path: str, body: Any) -> requests.Response: - url = "%s/%s" % (self.api_url, path) - logger.debug("POST: %s" % url) + url = f"{self.api_url}/{path}" + logger.debug(f"POST: {url}") resp = retry_call( self.request, fargs=[requests.post, url], @@ -76,25 +103,65 @@ def put(self, path: str, body: Any) -> requests.Response: raise SnykHTTPError(resp) return resp - def get(self, path: str) -> requests.Response: - url = "%s/%s" % (self.api_url, path) - logger.debug("GET: %s" % url) + def get( + self, path: str, params: dict = None, version: str = None + ) -> requests.Response: + """ + V3 Compatible Snyk Client, assumes the presence of Version, either set in the client + or called in this method means that we're talking to a V3 endpoint and will ensure the + params are encoded properly with the version. + + Since certain endpoints can exist only in certain versions, being able to override the + client version with each GET is necessary + + Returns a standard requests Response object + """ + + path = cleanup_path(path, logger) + + url = f"{self.api_url}/{path}" + + if params or self.version: + + if not params: + params = {} + + # we use the presence of version to determine if we are v3 or not + if "version" not in params.keys() and self.version: + params["version"] = version or self.version + + # Python Bools are True/False, JS Bools are true/false + # Snyk v3 API is strictly case sensitive at the moment + + for k, v in params.items(): + if isinstance(v, bool): + params[k] = str(v).lower() + + debug_url = f"{url}&{urllib.parse.urlencode(params)}" + fkwargs = {"headers": self.api_headers, "params": params} + else: + debug_url = url + fkwargs = {"headers": self.api_headers} + + logger.debug(f"GET: {debug_url}") + resp = retry_call( self.request, fargs=[requests.get, url], - fkwargs={"headers": self.api_headers}, + fkwargs=fkwargs, tries=self.tries, delay=self.delay, backoff=self.backoff, logger=logger, ) + if not resp: raise SnykHTTPError(resp) return resp def delete(self, path: str) -> requests.Response: - url = "%s/%s" % (self.api_url, path) - logger.debug("DELETE: %s" % url) + url = f"{self.api_url}/{path}" + logger.debug(f"DELETE: {url}") resp = retry_call( self.request, fargs=[requests.delete, url], @@ -108,6 +175,47 @@ def delete(self, path: str) -> requests.Response: raise SnykHTTPError(resp) return resp + def get_v3_pages(self, path: str, params: dict = {}) -> List: + """ + Helper function to collect paginated responses from the V3 API into a single + list. + + This collects the "data" list from the first reponse and then appends the + any further "data" lists if a next link is found in the links field. + """ + + # this is a raw primative but a higher level module might want something that does an + # arbitrary path + origin=foo + limit=100 url construction instead before being sent here + + limit = params["limit"] + + data = list() + + page = self.get(path, params).json() + + data.extend(page["data"]) + + while "next" in page["links"].keys(): + logger.debug(f"GET_V3_PAGES: Another link exists: {page['links']['next']}") + + next_url = urllib.parse.urlsplit(page["links"]["next"]) + query = urllib.parse.parse_qs(next_url.query) + + for k, v in query.items(): + params[k] = v + + params["limit"] = limit + + page = self.get(next_url.path, params).json() + + data.extend(page["data"]) + + logger.debug( + f"GET_V3_PAGES: Added another {len(page['data'])} items to the response" + ) + + return data + @property def organizations(self) -> Manager: return Manager.factory(Organization, self) diff --git a/snyk/models.py b/snyk/models.py index fd869ba..f854f7d 100644 --- a/snyk/models.py +++ b/snyk/models.py @@ -663,7 +663,8 @@ def _aggregated_issue_to_vulnerabily( try: upgradable_paths = filter( - lambda path: path[0].fixVersion is not None, issue_paths.paths, + lambda path: path[0].fixVersion is not None, + issue_paths.paths, ) first_path = next(upgradable_paths) upgrade_path = list(map(format_package, first_path)) diff --git a/snyk/test_client.py b/snyk/test_client.py index e9bfce0..bbce5b4 100644 --- a/snyk/test_client.py +++ b/snyk/test_client.py @@ -1,3 +1,4 @@ +import os import re import pytest # type: ignore @@ -6,6 +7,13 @@ from snyk.__version__ import __version__ from snyk.errors import SnykError, SnykNotFoundError from snyk.models import Organization, Project +from snyk.utils import load_test_data + +TEST_DATA = os.path.join(os.path.dirname(__file__), "test_data") + +V3_ORG = "39ddc762-b1b9-41ce-ab42-defbe4575bd6" +V3_URL = "https://api.snyk.io/v3" +V3_VERSION = "2022-02-16~experimental" class TestSnykClient(object): @@ -113,53 +121,11 @@ def test_empty_organizations(self, requests_mock, client): @pytest.fixture def organizations(self): - return { - "orgs": [ - { - "name": "defaultOrg", - "id": "689ce7f9-7943-4a71-b704-2ba575f01089", - "group": None, - "slug": "default-org", - "url": "https://api.snyk.io/org/default-org", - }, - { - "name": "My Other Org", - "id": "a04d9cbd-ae6e-44af-b573-0556b0ad4bd2", - "group": { - "name": "ACME Inc.", - "id": "a060a49f-636e-480f-9e14-38e773b2a97f", - }, - "slug": "my-other-org", - "url": "https://api.snyk.io/org/my-other-org", - }, - ] - } + return load_test_data(TEST_DATA, "organizations") @pytest.fixture def projects(self): - return { - "projects": [ - { - "name": "atokeneduser/goof", - "id": "6d5813be-7e6d-4ab8-80c2-1e3e2a454545", - "created": "2018-10-29T09:50:54.014Z", - "origin": "cli", - "type": "npm", - "readOnly": "false", - "isMonitored": "true", - "testFrequency": "daily", - "totalDependencies": 438, - "issueCountsBySeverity": { - "critical": 1, - "low": 8, - "high": 13, - "medium": 15, - }, - "lastTestedDate": "2019-02-05T06:21:00.000Z", - "browseUrl": "https://app.snyk.io/org/pysnyk-test-org/project/6d5813be-7e6d-4ab8-80c2-1e3e2a454545", - } - ] - } + return load_test_data(TEST_DATA, "projects") def test_loads_organizations(self, requests_mock, client, organizations): requests_mock.get("https://snyk.io/api/v1/orgs", json=organizations) @@ -234,3 +200,62 @@ def test_non_existent_project(self, requests_mock, client, organizations, projec requests_mock.get(matcher, json=projects) with pytest.raises(SnykNotFoundError): client.projects.get("not-present") + + @pytest.fixture + def v3client(self): + return SnykClient( + "token", version="2022-02-16~experimental", url="https://api.snyk.io/v3" + ) + + @pytest.fixture + def v3_groups(self): + return load_test_data(TEST_DATA, "v3_groups") + + @pytest.fixture + def v3_targets_page1(self): + return load_test_data(TEST_DATA, "v3_targets_page1") + + @pytest.fixture + def v3_targets_page2(self): + return load_test_data(TEST_DATA, "v3_targets_page2") + + @pytest.fixture + def v3_targets_page3(self): + return load_test_data(TEST_DATA, "v3_targets_page3") + + def test_v3get(self, requests_mock, v3client, v3_targets_page1): + requests_mock.get( + f"{V3_URL}/orgs/{V3_ORG}/targets?limit=10&version={V3_VERSION}", + json=v3_targets_page1, + ) + t_params = {"limit": 10} + + targets = v3client.get(f"orgs/{V3_ORG}/targets", t_params).json() + + assert len(targets["data"]) == 10 + + def test_get_v3_pages( + self, + requests_mock, + v3client, + v3_targets_page1, + v3_targets_page2, + v3_targets_page3, + ): + requests_mock.get( + f"{V3_URL}/orgs/{V3_ORG}/targets?limit=10&version={V3_VERSION}", + json=v3_targets_page1, + ) + requests_mock.get( + f"{V3_URL}/orgs/{V3_ORG}/targets?limit=10&version={V3_VERSION}&excludeEmpty=true&starting_after=v1.eyJpZCI6IjMyODE4ODAifQ%3D%3D", + json=v3_targets_page2, + ) + requests_mock.get( + f"{V3_URL}/orgs/{V3_ORG}/targets?limit=10&version={V3_VERSION}&excludeEmpty=true&starting_after=v1.eyJpZCI6IjI5MTk1NjgifQ%3D%3D", + json=v3_targets_page3, + ) + t_params = {"limit": 10} + + data = v3client.get_v3_pages(f"orgs/{V3_ORG}/targets", t_params) + + assert len(data) == 30 diff --git a/snyk/test_data/organizations.json b/snyk/test_data/organizations.json new file mode 100644 index 0000000..606e5d2 --- /dev/null +++ b/snyk/test_data/organizations.json @@ -0,0 +1,21 @@ +{ + "orgs": [ + { + "name": "defaultOrg", + "id": "689ce7f9-7943-4a71-b704-2ba575f01089", + "group": null, + "slug": "default-org", + "url": "https://api.snyk.io/org/default-org" + }, + { + "name": "My Other Org", + "id": "a04d9cbd-ae6e-44af-b573-0556b0ad4bd2", + "group": { + "name": "ACME Inc.", + "id": "a060a49f-636e-480f-9e14-38e773b2a97f" + }, + "slug": "my-other-org", + "url": "https://api.snyk.io/org/my-other-org" + } + ] +} \ No newline at end of file diff --git a/snyk/test_data/projects.json b/snyk/test_data/projects.json new file mode 100644 index 0000000..f5779a1 --- /dev/null +++ b/snyk/test_data/projects.json @@ -0,0 +1,23 @@ +{ + "projects": [ + { + "name": "atokeneduser/goof", + "id": "6d5813be-7e6d-4ab8-80c2-1e3e2a454545", + "created": "2018-10-29T09:50:54.014Z", + "origin": "cli", + "type": "npm", + "readOnly": "false", + "isMonitored": "true", + "testFrequency": "daily", + "totalDependencies": 438, + "issueCountsBySeverity": { + "critical": 1, + "low": 8, + "high": 13, + "medium": 15 + }, + "lastTestedDate": "2019-02-05T06:21:00.000Z", + "browseUrl": "https://app.snyk.io/org/pysnyk-test-org/project/6d5813be-7e6d-4ab8-80c2-1e3e2a454545" + } + ] +} \ No newline at end of file diff --git a/snyk/test_data/v3_groups.json b/snyk/test_data/v3_groups.json new file mode 100644 index 0000000..70e0b21 --- /dev/null +++ b/snyk/test_data/v3_groups.json @@ -0,0 +1,22 @@ +{ + "jsonapi": { + "version": "1.0" + }, + "data": [ + { + "type": "group", + "id": "36863d40-ba29-491f-af63-7a1a7d79e411", + "attributes": { + "name": "Customer Success Engineering" + } + }, + { + "type": "group", + "id": "164f8e6d-ea58-45ef-a638-259cc0fe90d4", + "attributes": { + "name": "Goof Ltd (Enterprise)" + } + } + ], + "links": {} +} \ No newline at end of file diff --git a/snyk/test_data/v3_targets_page1.json b/snyk/test_data/v3_targets_page1.json new file mode 100644 index 0000000..67359ef --- /dev/null +++ b/snyk/test_data/v3_targets_page1.json @@ -0,0 +1,120 @@ +{ + "jsonapi": { + "version": "1.0" + }, + "data": [ + { + "type": "target", + "id": "44948be7-561c-4e03-80af-27232eaea006", + "attributes": { + "isPrivate": true, + "origin": "cli", + "displayName": "snyk-playground/maven-multi-with-gh-prevent", + "remoteUrl": "http://github.com/snyk-playground/maven-multi-with-gh-prevent.git" + }, + "relationships": {} + }, + { + "type": "target", + "id": "c3035b3b-9416-4d00-9e49-bdb561560ea8", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/maven-multi-with-gh-prevent", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "b17a13c7-25f4-4360-bba5-d1098efb579c", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-tech-services/snyk-project-tldr", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "d3ce9715-6cd8-4940-b326-5fa7d35988f2", + "attributes": { + "isPrivate": true, + "origin": "cli", + "displayName": "snyk-tech-services/snyk-bulk-yarn", + "remoteUrl": "http://github.com/snyk-tech-services/snyk-bulk-yarn" + }, + "relationships": {} + }, + { + "type": "target", + "id": "3d8fd759-1fdf-4827-91a7-8706d54462de", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/sync-aws-deploy", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "f26b4ae2-59e5-4be9-978d-0b2157d04d7c", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/bazel-examples", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "1aae46bd-13e1-4c6c-9cbe-8849732ec00e", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/example-bazel-monorepo", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "f6017bfd-e441-4482-b060-78e56daff65c", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/docker-goof", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "84519645-6a5a-4b0f-9132-a65e76d3aa03", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/monorepo-kotlin", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "dc2aa733-550d-43dd-9066-e9a35b6f3982", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/org-import-branch-override", + "remoteUrl": null + }, + "relationships": {} + } + ], + "links": { + "next": "/orgs/39ddc762-b1b9-41ce-ab42-defbe4575bd6/targets?limit=10&version=2022-02-16~experimental&excludeEmpty=true&starting_after=v1.eyJpZCI6IjMyODE4ODAifQ%3D%3D" + } +} \ No newline at end of file diff --git a/snyk/test_data/v3_targets_page2.json b/snyk/test_data/v3_targets_page2.json new file mode 100644 index 0000000..0008af9 --- /dev/null +++ b/snyk/test_data/v3_targets_page2.json @@ -0,0 +1,121 @@ +{ + "jsonapi": { + "version": "1.0" + }, + "data": [ + { + "type": "target", + "id": "9a181724-d17d-4a92-a85c-72594c0df5e1", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/org-import-instance", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "9aafc91d-76c1-4279-aa27-6e648130f003", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/pygithub-import-parser", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "bcb30f5f-ae18-4d32-ac62-1459b10050ae", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/org-project-import", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "08beb7e6-ccdf-437a-95dd-9f3c8ad66cd8", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/repo-with-jira-config", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "e29608d3-75dc-46ad-8ef4-3bf3c0331f35", + "attributes": { + "isPrivate": true, + "origin": "github", + "displayName": "snyk-playground/multi-project-code", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "f94abd6f-d436-4aa8-b4d0-51523a7224c5", + "attributes": { + "isPrivate": true, + "origin": "github", + "displayName": "scotte-snyk/example-yarn", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "b248a8b5-98a6-4a08-831c-748776ebc1d9", + "attributes": { + "isPrivate": true, + "origin": "github", + "displayName": "scotte-snyk/test-ruby-project", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "53c9a6b1-a46c-44a3-a11f-296b9bd88e1e", + "attributes": { + "isPrivate": true, + "origin": "github", + "displayName": "scotte-snyk/demo-sonarr-renamed", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "0e8f2413-af32-45a2-92f5-b9f8ada48374", + "attributes": { + "isPrivate": true, + "origin": "github", + "displayName": "scotte-snyk/vulnerable-php-app", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "e52ec5a2-6b11-457e-a8fc-4d86e6fbfab1", + "attributes": { + "isPrivate": true, + "origin": "cli", + "displayName": "kozmer/log4j-shell-poc", + "remoteUrl": "http://github.com/kozmer/log4j-shell-poc.git" + }, + "relationships": {} + } + ], + "links": { + "next": "/orgs/39ddc762-b1b9-41ce-ab42-defbe4575bd6/targets?limit=10&version=2022-02-16~experimental&excludeEmpty=true&starting_after=v1.eyJpZCI6IjI5MTk1NjgifQ%3D%3D", + "prev": "/orgs/39ddc762-b1b9-41ce-ab42-defbe4575bd6/targets?limit=10&version=2022-02-16~experimental&excludeEmpty=true&ending_before=v1.eyJpZCI6IjMyODE4NzkifQ%3D%3D" + } +} \ No newline at end of file diff --git a/snyk/test_data/v3_targets_page3.json b/snyk/test_data/v3_targets_page3.json new file mode 100644 index 0000000..f23acbb --- /dev/null +++ b/snyk/test_data/v3_targets_page3.json @@ -0,0 +1,120 @@ +{ + "jsonapi": { + "version": "1.0" + }, + "data": [ + { + "type": "target", + "id": "44948be7-561c-4e03-80af-27232eaea006", + "attributes": { + "isPrivate": true, + "origin": "cli", + "displayName": "snyk-playground/maven-multi-with-gh-prevent", + "remoteUrl": "http://github.com/snyk-playground/maven-multi-with-gh-prevent.git" + }, + "relationships": {} + }, + { + "type": "target", + "id": "c3035b3b-9416-4d00-9e49-bdb561560ea8", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/maven-multi-with-gh-prevent", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "b17a13c7-25f4-4360-bba5-d1098efb579c", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-tech-services/snyk-project-tldr", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "d3ce9715-6cd8-4940-b326-5fa7d35988f2", + "attributes": { + "isPrivate": true, + "origin": "cli", + "displayName": "snyk-tech-services/snyk-bulk-yarn", + "remoteUrl": "http://github.com/snyk-tech-services/snyk-bulk-yarn" + }, + "relationships": {} + }, + { + "type": "target", + "id": "3d8fd759-1fdf-4827-91a7-8706d54462de", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/sync-aws-deploy", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "f26b4ae2-59e5-4be9-978d-0b2157d04d7c", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/bazel-examples", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "1aae46bd-13e1-4c6c-9cbe-8849732ec00e", + "attributes": { + "isPrivate": false, + "origin": "github-enterprise", + "displayName": "snyk-playground/example-bazel-monorepo", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "f6017bfd-e441-4482-b060-78e56daff65c", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/docker-goof", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "84519645-6a5a-4b0f-9132-a65e76d3aa03", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/monorepo-kotlin", + "remoteUrl": null + }, + "relationships": {} + }, + { + "type": "target", + "id": "dc2aa733-550d-43dd-9066-e9a35b6f3982", + "attributes": { + "isPrivate": true, + "origin": "github-enterprise", + "displayName": "snyk-playground/org-import-branch-override", + "remoteUrl": null + }, + "relationships": {} + } + ], + "links": { + "prev": "/orgs/39ddc762-b1b9-41ce-ab42-defbe4575bd6/targets?limit=10&version=2022-02-16~experimental&excludeEmpty=true&ending_before=v1.eyJpZCI6IjMyODE4ODAifQ%3D%3D" + } +} \ No newline at end of file diff --git a/snyk/test_models.py b/snyk/test_models.py index f8dbd4c..62e7cec 100644 --- a/snyk/test_models.py +++ b/snyk/test_models.py @@ -483,13 +483,15 @@ def test_empty_issues(self, project, project_url, requests_mock): def test_empty_issues_aggregated(self, project, project_url, requests_mock): requests_mock.post( - "%s/aggregated-issues" % project_url, json={"issues": []}, + "%s/aggregated-issues" % project_url, + json={"issues": []}, ) assert [] == project.issueset_aggregated.all().issues def test_empty_vulnerabilities(self, project, project_url, requests_mock): requests_mock.post( - "%s/aggregated-issues" % project_url, json={"issues": []}, + "%s/aggregated-issues" % project_url, + json={"issues": []}, ) assert [] == project.vulnerabilities @@ -573,7 +575,10 @@ def test_vulnerabilities(self, project, project_url, requests_mock): "snapshotId": "bb00717d-4618-4ceb-bebd-ec268a563e98", "paths": [ [ - {"name": "tap", "version": "11.1.5",}, + { + "name": "tap", + "version": "11.1.5", + }, {"name": "nyc", "version": "11.9.0"}, {"name": "istanbul-lib-instrument", "version": "1.10.1"}, {"name": "babel-traverse", "version": "6.26.0"}, @@ -644,7 +649,8 @@ def test_filtering_empty_issues_aggregated( self, project, project_url, requests_mock ): requests_mock.post( - "%s/aggregated-issues" % project_url, json={"issues": []}, + "%s/aggregated-issues" % project_url, + json={"issues": []}, ) assert [] == project.issueset_aggregated.filter(ignored=True).issues diff --git a/snyk/utils.py b/snyk/utils.py index fa9cdc1..4713c29 100644 --- a/snyk/utils.py +++ b/snyk/utils.py @@ -1,4 +1,5 @@ -import re +import json +import logging from itertools import chain @@ -19,3 +20,33 @@ def flat_map(fn, *args): def format_package(pkg): return "{name}@{version}".format(name=pkg.name, version=pkg.version or "*") + + +def cleanup_path(path: str, logger: logging.Logger) -> str: + """ + Strings '/' from the start and end of strings if present to ensure that a '//' doesn't + occur in an API request due to copy/paste error + """ + + if path[0] == "/": + logger.warn(f"removing unneccessary leading / from {path}") + path = path[1:] + if path[-1] == "/": + logger.warn(f"removing unneccessary trailing / from {path}") + path = path.rstrip("/") + + return path + + +def load_test_data(test_dir: str, test_name: str) -> dict: + """ + Returns the contents of a json file at location of: + test_dir/test_name.json + + This is meant to keep large amounts of json needed for testing outside of + the tests themselves and as the actual json responses from the API + """ + test_file = f"{test_dir}/{test_name}.json" + with open(test_file, "r") as the_file: + data = the_file.read() + return json.loads(data)