diff --git a/.codecov.yml b/.codecov.yml index b2ded03..03732ac 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -1,4 +1,4 @@ coverage: range: "90...100" round: nearest - precision: 0 \ No newline at end of file + precision: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 20859ea..4bc77ad 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,11 +26,17 @@ jobs: - name: Lint run: nox -s lint if: matrix.os == 'ubuntu-latest' && matrix.python == '3.10' + - name: Scan packages for vulnerabilities + run: nox -s safety + if: matrix.os == 'ubuntu-latest' && matrix.python == '3.10' - name: CPython tests run: nox -s tests-${{ matrix.python }} if: matrix.python != 'pypy-3.7' - env: - CODECOV_TOKEN: ${{ secrets.codecov_token }} - name: Pypy tests run: nox -s tests-pypy3 - if: matrix.python == 'pypy-3.7' \ No newline at end of file + if: matrix.python == 'pypy-3.7' + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v2 + if: matrix.python == '3.10' + with: + token: ${{ secrets.codecov_token }} diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4b9897d..7290293 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -21,4 +21,4 @@ jobs: - name: Build and deploy run: nox -s deploy env: - POETRY_PYPI_TOKEN_PYPI: ${{ secrets.pypi_token }} \ No newline at end of file + POETRY_PYPI_TOKEN_PYPI: ${{ secrets.pypi_token }} diff --git a/.gitignore b/.gitignore index cf6ca59..8f6705a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ __pycache__ # packaging / distribution site dist -build \ No newline at end of file +build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..4fcfc59 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,34 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.1.0 + hooks: + - id: check-yaml + - id: end-of-file-fixer + - id: trailing-whitespace + - repo: local + hooks: + - id: isort + name: isort + entry: poetry run isort click_params tests + language: system + types: + - python + - id: black + name: black + entry: poetry run black click_params tests + language: system + types: + - python + - id: bandit + name: bandit + entry: poetry run bandit -r click_params + language: system + exclude: ^tests/ + types: + - python + - id: flake8 + name: flake8 + entry: poetry run flake8 + language: system + types: + - python diff --git a/.readthedocs.yml b/.readthedocs.yml index a320d63..cc38964 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -5,4 +5,4 @@ mkdocs: python: install: - - requirements: docs/requirements.txt \ No newline at end of file + - requirements: docs/requirements.txt diff --git a/CHANGELOG.md b/CHANGELOG.md index f202f03..c5c5d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.4.0] - 2022-08-13 + +### Added + +- All `ListParamType` subclasses have a new parameter `ignore_empty` that defaults to False. + +### Changed + +- Renamed `UnionParamType` to `FirstOf` and changed its signature. + ## [0.3.0] - 2022-03-14 -## Fixed +### Fixed - An issue when a user is prompted a value for a type inheriting `ListParamType`. @@ -35,11 +45,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Changed .travis.yml and appveyor.yml to take in account nox. - + ### Removed - Removed pipenv in favor of poetry. ## [0.1.0] - 2019-07-21 ### Added -- First release of the package. \ No newline at end of file +- First release of the package. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 55fc454..f4ff249 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,14 +6,15 @@ Contributions of all kinds are welcome. ## Bug reports -To submit a bug report, please use the [issue tracker](https://github.com/lewoudar/click_params/issues). +To submit a bug report, please use the [issue tracker](https://github.com/click-contrib/click-params/issues). -Before trying to submit a pull request, make sure that there is not already a similar issue reported. +Before trying to submit an issue, make sure that there is not already a similar one reported. -Your message should contains as much information as possible such as: -- the environment in which you worked (OS name and version, python version, etc..) +Your message should contain as much information as possible such as: + +- the environment in which you worked (OS name and version, python version, etc...) - Guidance on how to reproduce the issue. For example a small piece of code that can be run immediately. -- Tell me **what you mean by failure** i.e what are you expecting to happen. +- Tell me **what you mean by failure** i.e. what are you expecting to happen. # Fix a bug @@ -22,56 +23,58 @@ correct it. Look at [code contribution](#code-contribution) section before submi ## Submit feedback -To submit feedback, again use the [issue tracker](https://github.com/lewoudar/click_params/issues). +To submit feedback, again use the [issue tracker](https://github.com/click-contrib/click-params/issues). -If you want to propose a new feature: -- explain in detail how it would work. -- keep the scope as narrow as possible to make it easier to develop. -- if you want to propose code, just read below the section [code contribution](#code-contribution). +## Code style -## Documentation +Here are some preferences I have when coding: -The documentation is never a simple part to write, if you feel that it can be improved, feel free to propose a pull -request. Just a few things to be aware of: -- The documentation is built with [mkdocs](https://www.mkdocs.org/) using the markdown syntax. -- Try to follow the style of the documentation, for example do not have lines longer than 120 characters, use -single-quotes strings ('hello' instead of "hello") when presenting python code. -- Look at the [code contribution](#code-contribution) section for how to setup the project and commit convention. +- Use single quotes wherever possible. Unfortunately `black` does not allow to enforce this rule. +- When writing a test function, the name should be as obvious as possible about what we want to test. I prefer the + formulation "test_should....when..". I'm not saying it is the best option all the time, but it + often tends to be more readable. ## Code contribution -Ready to contribute? Here is how to setup the project for local development. -1. Fork the click_params repo on Github. +Ready to contribute? Here is how to set up the project for local development. + +1. Fork the click_params repo on GitHub. -2. Clone your fork locally. If you don't know how to proceed, - take a look at this [article](https://help.github.com/en/articles/fork-a-repo). +2. Clone your fork locally. If you don't know how to proceed, take a look at + this [article](https://help.github.com/en/articles/fork-a-repo). 3. Install the dependencies needed by the project. You must first install [poetry](https://python-poetry.org/docs/) on your computer. After that, you can run: - ```bash + ```shell poetry install ``` -4. Create a branch for local development - ```bash +4. Install the pre-commit hooks + ```shell + pre-commit install + ``` + +5. Create a branch for local development + ```shell git checkout -b name-of-bug-or-feature ``` -5. When you are done with your work, you need to pass all the tests using [nox](https://nox.thea.codes/en/stable/). - ```bash +6. Make sure you respect the [code style](#code-style) when developing. + +7. When you are done with your work, you need to pass all the tests using [nox](https://nox.thea.codes/en/stable/). + ```shell nox -s lint tests ``` -6. Commit your changes and push your branch to GitHub. - For the commit message, you should use the convention described [here](https://medium.com/@menuka/writing-meaningful-git-commit-messages-a62756b65c81). - It is the convention developed by the angular project. There is just one notable difference I'm adding. - The verb must be conjugated **with the past tense** because I believe we are talking about a done action and not an - action to be performed. - Also for the scope, there is no particular set of scopes, so feel free to add what you thinks suits well your - changes. If you don't have one in mind, don't put anything. - -7. Before submitting the pull request, you should verify that you include tests. There is also a code coverage - configured with the project. You can check the pull request status to know if your tests cover all the code you wrote. - If your pull request add functionality, please update the documentation. - -8. Submit your pull request through the GitHub website. \ No newline at end of file +8. Commit your changes and push your branch to GitHub. For the commit message, you should use the convention described + [here](https://medium.com/@menuka/writing-meaningful-git-commit-messages-a62756b65c81). It is the convention + developed by the angular project. There is just one notable difference I'm adding. The verb must be conjugated **in + the past tense** because I believe we are talking about a done action and not an action to be performed. Also, for + the scope, there is no particular set of scopes, so feel free to add what you think suits well your changes. If you + don't have one in mind, don't put anything. + +9. Before submitting the pull request, you should verify that you include tests. There is also a code coverage + configured with the project. You can check the pull request status to know if your tests cover all the code you + wrote. If your pull request add functionality, please update the documentation. + +10. Submit your pull request through the GitHub website. diff --git a/LICENSE b/LICENSE index 7a4a3ea..d645695 100644 --- a/LICENSE +++ b/LICENSE @@ -199,4 +199,4 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/README.md b/README.md index 3e36fd3..bdf4cb9 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ [![Coverage Status](https://codecov.io/gh/click-contrib/click_params/branch/master/graphs/badge.svg?branch=master)](https://codecov.io/gh/click-contrib/click_params) [![Documentation Status](https://readthedocs.org/projects/click_params/badge/?version=latest)](https://click-params.readthedocs.io/en/latest/?badge=latest) [![License Apache 2](https://img.shields.io/hexpm/l/plug.svg)](http://www.apache.org/licenses/LICENSE-2.0) +[![Code Style](https://img.shields.io/badge/code%20style-black-black)](https://github.com/click-contrib/click_params) A bunch of useful click parameter types. @@ -51,4 +52,3 @@ You can change the default separator "," by passing it when initializing the par ## Documentation Documentation is available at https://click-params.readthedocs.io/en/latest/. - diff --git a/click_params/__init__.py b/click_params/__init__.py index 02d249e..860ee96 100644 --- a/click_params/__init__.py +++ b/click_params/__init__.py @@ -1,46 +1,113 @@ __version__ = '0.2.0' -from .base import BaseParamType, ValidatorParamType, RangeParamType, ListParamType +from .base import BaseParamType, ListParamType, RangeParamType, ValidatorParamType from .domain import ( - DOMAIN, PUBLIC_URL, URL, EMAIL, SLUG, EmailParamType, DomainListParamType, PublicUrlListParamType, - UrlListParamType, EmailListParamType, SlugListParamType + DOMAIN, + EMAIL, + PUBLIC_URL, + SLUG, + URL, + DomainListParamType, + EmailListParamType, + EmailParamType, + PublicUrlListParamType, + SlugListParamType, + UrlListParamType, ) -from .miscellaneous import ( - JSON, MAC_ADDRESS, StringListParamType, ChoiceListParamType, MacAddressListParamType, UUIDListParamType, - DateTimeListParamType, FirstOf +from .miscellaneous import ( # ChoiceListParamType, + JSON, + MAC_ADDRESS, + DateTimeListParamType, + FirstOf, + MacAddressListParamType, + StringListParamType, + UUIDListParamType, ) from .network import ( - IP_ADDRESS, IPV4_ADDRESS, IPV6_ADDRESS, IP_NETWORK, IPV4_NETWORK, IPV6_NETWORK, Ipv4AddressRange, Ipv6AddressRange, - IpAddressListParamType, Ipv4AddressListParamType, Ipv6AddressListParamType, IpNetworkListParamType, - Ipv4NetworkListParamType, Ipv6NetworkListParamType + IP_ADDRESS, + IP_NETWORK, + IPV4_ADDRESS, + IPV4_NETWORK, + IPV6_ADDRESS, + IPV6_NETWORK, + IpAddressListParamType, + IpNetworkListParamType, + Ipv4AddressListParamType, + Ipv4AddressRange, + Ipv4NetworkListParamType, + Ipv6AddressListParamType, + Ipv6AddressRange, + Ipv6NetworkListParamType, ) from .numeric import ( - COMPLEX, FRACTION, DECIMAL, DecimalRange, FractionRange, IntListParamType, FloatListParamType, - FractionListParamType, DecimalListParamType, ComplexListParamType + COMPLEX, + DECIMAL, + FRACTION, + ComplexListParamType, + DecimalListParamType, + DecimalRange, + FloatListParamType, + FractionListParamType, + FractionRange, + IntListParamType, ) -from .test_utils import assert_list_in_output, assert_equals_output, assert_in_output +from .test_utils import assert_equals_output, assert_in_output, assert_list_in_output __all__ = [ # base - 'BaseParamType', 'ValidatorParamType', 'RangeParamType', 'ListParamType', - + 'BaseParamType', + 'ValidatorParamType', + 'RangeParamType', + 'ListParamType', # domain - 'DOMAIN', 'PUBLIC_URL', 'URL', 'EmailParamType', 'EMAIL', 'SLUG', 'DomainListParamType', 'PublicUrlListParamType', - 'UrlListParamType', 'EmailListParamType', 'SlugListParamType', - + 'DOMAIN', + 'PUBLIC_URL', + 'URL', + 'EmailParamType', + 'EMAIL', + 'SLUG', + 'DomainListParamType', + 'PublicUrlListParamType', + 'UrlListParamType', + 'EmailListParamType', + 'SlugListParamType', # miscellaneous - 'JSON', 'MAC_ADDRESS', 'ChoiceListParamType', 'StringListParamType', 'MacAddressListParamType', 'UUIDListParamType', - 'DateTimeListParamType', 'FirstOf', - + 'JSON', + 'MAC_ADDRESS', + # 'ChoiceListParamType', + 'StringListParamType', + 'MacAddressListParamType', + 'UUIDListParamType', + 'DateTimeListParamType', + 'FirstOf', # network - 'IP_ADDRESS', 'IPV6_ADDRESS', 'IPV4_ADDRESS', 'IP_NETWORK', 'IPV4_NETWORK', 'IPV6_NETWORK', 'Ipv4AddressRange', - 'Ipv6AddressRange', 'IpAddressListParamType', 'Ipv4AddressListParamType', 'Ipv6AddressListParamType', - 'Ipv4NetworkListParamType', 'IpNetworkListParamType', 'Ipv6NetworkListParamType', - + 'IP_ADDRESS', + 'IPV6_ADDRESS', + 'IPV4_ADDRESS', + 'IP_NETWORK', + 'IPV4_NETWORK', + 'IPV6_NETWORK', + 'Ipv4AddressRange', + 'Ipv6AddressRange', + 'IpAddressListParamType', + 'Ipv4AddressListParamType', + 'Ipv6AddressListParamType', + 'Ipv4NetworkListParamType', + 'IpNetworkListParamType', + 'Ipv6NetworkListParamType', # numeric - 'FRACTION', 'FractionRange', 'DECIMAL', 'DecimalRange', 'COMPLEX', 'IntListParamType', 'FloatListParamType', - 'FractionListParamType', 'DecimalListParamType', 'ComplexListParamType', - + 'FRACTION', + 'FractionRange', + 'DECIMAL', + 'DecimalRange', + 'COMPLEX', + 'IntListParamType', + 'FloatListParamType', + 'FractionListParamType', + 'DecimalListParamType', + 'ComplexListParamType', # test_utils - 'assert_equals_output', 'assert_in_output', 'assert_list_in_output' + 'assert_equals_output', + 'assert_in_output', + 'assert_list_in_output', ] diff --git a/click_params/base.py b/click_params/base.py index 87da6e5..0ee8e7a 100644 --- a/click_params/base.py +++ b/click_params/base.py @@ -1,9 +1,9 @@ """Base classes to implement various parameter types""" -from typing import Union, Tuple, Callable, List, Any, Optional +from typing import Any, Callable, List, Optional, Tuple, Union import click -from .annotations import Min, Max, Error +from .annotations import Error, Max, Min class CustomParamType(click.ParamType): @@ -46,7 +46,6 @@ def __repr__(self): class RangeParamType(CustomParamType): - def __init__(self, param_type: click.ParamType, minimum: Min = None, maximum: Max = None, clamp: bool = False): self._minimum = minimum self._maximum = maximum @@ -70,8 +69,9 @@ def convert(self, value, param, ctx): elif self._maximum is None: self.fail(f'{converted_value} is smaller than the minimum valid value {self._minimum}.', param, ctx) else: - self.fail(f'{converted_value} is not in the valid range of {self._minimum} to {self._maximum}.', - param, ctx) + self.fail( + f'{converted_value} is not in the valid range of {self._minimum} to {self._maximum}.', param, ctx + ) return converted_value def __repr__(self): @@ -82,14 +82,7 @@ def __repr__(self): class ListParamType(CustomParamType): - - def __init__( - self, - param_type: click.ParamType, - separator: str = ',', - name: str = None, - ignore_empty: bool = False - ): + def __init__(self, param_type: click.ParamType, separator: str = ',', name: str = None, ignore_empty: bool = False): if not isinstance(separator, str): raise TypeError('separator must be a string') self._separator = separator diff --git a/click_params/domain.py b/click_params/domain.py index d4e13d9..6796fdb 100644 --- a/click_params/domain.py +++ b/click_params/domain.py @@ -2,9 +2,9 @@ from functools import partial from typing import List -from validators import domain, url, email, slug +from validators import domain, email, slug, url -from .base import ValidatorParamType, ListParamType +from .base import ListParamType, ValidatorParamType class DomainParamType(ValidatorParamType): diff --git a/click_params/miscellaneous.py b/click_params/miscellaneous.py index 8406994..ee56dea 100644 --- a/click_params/miscellaneous.py +++ b/click_params/miscellaneous.py @@ -6,15 +6,22 @@ import click from validators import mac_address -from .base import ValidatorParamType, ListParamType, CustomParamType +from .base import CustomParamType, ListParamType, ValidatorParamType class JsonParamType(click.ParamType): name = 'json' - def __init__(self, cls: Callable = None, object_hook: Callable = None, parse_float: Callable = None, - parse_int: Callable = None, parse_constant: Callable = None, object_pairs_hook: Callable = None, - **kwargs): + def __init__( + self, + cls: Callable = None, + object_hook: Callable = None, + parse_float: Callable = None, + parse_int: Callable = None, + parse_constant: Callable = None, + object_pairs_hook: Callable = None, + **kwargs, + ): self._cls = cls self._object_hook = object_hook self._parse_float = parse_float @@ -25,9 +32,16 @@ def __init__(self, cls: Callable = None, object_hook: Callable = None, parse_flo def convert(self, value, param, ctx): try: - return json.loads(value, cls=self._cls, object_hook=self._object_hook, parse_float=self._parse_float, - parse_int=self._parse_int, parse_constant=self._parse_constant, - object_pairs_hook=self._object_pairs_hook, **self._kwargs) + return json.loads( + value, + cls=self._cls, + object_hook=self._object_hook, + parse_float=self._parse_float, + parse_int=self._parse_int, + parse_constant=self._parse_constant, + object_pairs_hook=self._object_pairs_hook, + **self._kwargs, + ) except json.JSONDecodeError: self.fail(f'{value} is not a valid json string', param, ctx) @@ -91,9 +105,7 @@ def __init__(self, *param_types: click.ParamType, name: Optional[str] = None, re # Using pipe | as that is used by python sets for union. self.name = '(' + ' | '.join(p.name for p in self.param_types) + ')' - def convert( - self, value: str, param: Optional[click.Parameter], ctx: Optional[click.Context] - ) -> Any: + def convert(self, value: str, param: Optional[click.Parameter], ctx: Optional[click.Context]) -> Any: # Collect failure messages to emit later. fails: List[Tuple[click.ParamType, str]] = [] for param_type in self.param_types: @@ -108,8 +120,7 @@ def convert( + "\n - ".join( [ indent( - f"{getattr(f[0], 'name', f[0].__class__.__name__).upper()}:" - f' {f[1]}', + f"{getattr(f[0], 'name', f[0].__class__.__name__).upper()}:" f' {f[1]}', ' ', ) for f in fails diff --git a/click_params/network.py b/click_params/network.py index b3ee0a7..afb0ab1 100644 --- a/click_params/network.py +++ b/click_params/network.py @@ -1,7 +1,7 @@ """Network parameter types""" import ipaddress -from .base import BaseParamType, RangeParamType, ListParamType +from .base import BaseParamType, ListParamType, RangeParamType class IpAddress(BaseParamType): @@ -28,8 +28,9 @@ def __init__(self): class Ipv4AddressRange(RangeParamType): name = 'ipv4 address range' - def __init__(self, minimum: ipaddress.IPv4Address = None, maximum: ipaddress.IPv4Address = None, - clamp: bool = False): + def __init__( + self, minimum: ipaddress.IPv4Address = None, maximum: ipaddress.IPv4Address = None, clamp: bool = False + ): super().__init__(Ipv4Address(), minimum, maximum, clamp) def __repr__(self): @@ -53,8 +54,9 @@ def __init__(self): class Ipv6AddressRange(RangeParamType): name = 'ipv6 address range' - def __init__(self, minimum: ipaddress.IPv6Address = None, maximum: ipaddress.IPv6Address = None, - clamp: bool = False): + def __init__( + self, minimum: ipaddress.IPv6Address = None, maximum: ipaddress.IPv6Address = None, clamp: bool = False + ): super().__init__(Ipv6Address(), minimum, maximum, clamp) def __repr__(self): diff --git a/click_params/numeric.py b/click_params/numeric.py index 3328301..9f2ffaf 100644 --- a/click_params/numeric.py +++ b/click_params/numeric.py @@ -4,7 +4,7 @@ import click -from .base import BaseParamType, RangeParamType, ListParamType +from .base import BaseParamType, ListParamType, RangeParamType class DecimalParamType(BaseParamType): @@ -60,27 +60,21 @@ class ComplexListParamType(ListParamType): name = 'complex list' def __init__(self, separator: str = ',', ignore_empty: bool = False): - super().__init__( - COMPLEX, separator=separator, name='complex values', ignore_empty=ignore_empty - ) + super().__init__(COMPLEX, separator=separator, name='complex values', ignore_empty=ignore_empty) class IntListParamType(ListParamType): name = 'int list' def __init__(self, separator: str = ',', ignore_empty: bool = False): - super().__init__( - click.INT, separator=separator, name='integers', ignore_empty=ignore_empty - ) + super().__init__(click.INT, separator=separator, name='integers', ignore_empty=ignore_empty) class FloatListParamType(ListParamType): name = 'float list' def __init__(self, separator: str = ',', ignore_empty: bool = False): - super().__init__( - click.FLOAT, separator=separator, name='floating point values', ignore_empty=ignore_empty - ) + super().__init__(click.FLOAT, separator=separator, name='floating point values', ignore_empty=ignore_empty) DECIMAL = DecimalParamType() diff --git a/docs/api.md b/docs/api.md index 073a0a5..a86e2ef 100644 --- a/docs/api.md +++ b/docs/api.md @@ -78,7 +78,7 @@ the necessary tests in your code ;) Signature: `RangeParamType(param_type: click.ParamType, minimum: Min = None, maximum: Max = None, clamp: bool = False)` -This class helps to create types where values needed to be bounded. The `minimum` and `maximum` parameters **must** have +This class helps to create types where values needed to be bounded. The `minimum` and `maximum` parameters **must** have the **same type** and the type **must** implement comparison operators (<, >, ..). Parameters: @@ -106,7 +106,7 @@ class IntRange(RangeParamType): ```` !!! note - If you look at the `__repr__` implementation of the `RangeParamType`, you will notice that it expects a `name` + If you look at the `__repr__` implementation of the `RangeParamType`, you will notice that it expects a `name` attribute with two words separated by a whitespace. If you don't want this behaviour you can override this method. ## ListParamType @@ -135,4 +135,4 @@ class IntListParamType(ListParamType): def __init__(self, separator: str = ','): super().__init__(click.INT, separator=separator, name='integers') -```` \ No newline at end of file +```` diff --git a/docs/changelog.md b/docs/changelog.md index 59bd407..15fbc92 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -19,11 +19,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Changed .travis.yml and appveyor.yml to take in account nox. - + ### Removed - Removed pipenv in favor of poetry. ## [0.1.0] - 2019-07-21 ### Added -- First release of the package. \ No newline at end of file +- First release of the package. diff --git a/docs/installation.md b/docs/installation.md index 4001df6..bc04861 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -14,4 +14,4 @@ The project has a few dependencies: - [validators](https://validators.readthedocs.io/en/latest/) !!! note - click-params works starting from **python 3.7** \ No newline at end of file + click-params works starting from **python 3.7** diff --git a/docs/requirements.txt b/docs/requirements.txt index f21371f..c005689 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,3 +1,3 @@ click==8.0.0 validators==0.18.2 -mkdocs-material==7.1.4 \ No newline at end of file +mkdocs-material==8.4.0 diff --git a/docs/tests.md b/docs/tests.md index e048465..568948c 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -39,9 +39,9 @@ present. If we consider again the previous command, if we want to test the case where the name entered by the user is _John_, it will not be convenient to test the entire error message returned by the click command because a large part is generated -by click itself and it is irrelevant: `"Usage... Error.."`. +by click itself and it is irrelevant: `"Usage... Error.."`. -Instead, we would prefer to focus on the error message we explicitly send to the user, in this case: +Instead, we would prefer to focus on the error message we explicitly send to the user, in this case: `"the name is incorrect"` ````python @@ -81,6 +81,6 @@ from my_project.scripts import cli def test_echo_json(): runner = CliRunner() result = runner.invoke(cli) - + assert_list_in_output(0, ['name', 'Kevin T', 'age', 25], result) ```` diff --git a/docs/usage/domain.md b/docs/usage/domain.md index 73976a5..0495596 100644 --- a/docs/usage/domain.md +++ b/docs/usage/domain.md @@ -69,7 +69,7 @@ Your list of domain names: $ python cli.py --domains='foo bar.com bar' Error: These items are not domain names: ['foo', 'bar'] ```` - + ## PUBLIC_URL Validates that a string is a regular url. @@ -280,4 +280,4 @@ Your list of slugs: $ python cli.py --slugs='foo .foo foo-bar' Error: These items are not slugs: ['.foo'] -```` \ No newline at end of file +```` diff --git a/docs/usage/miscellaneous.md b/docs/usage/miscellaneous.md index bd419b2..8cb58af 100644 --- a/docs/usage/miscellaneous.md +++ b/docs/usage/miscellaneous.md @@ -112,6 +112,9 @@ Your list of preferred fruits: - pineapples - strawberries ```` + ## UUIDListParamType @@ -251,4 +255,4 @@ Error: Invalid value for '-j' / '--jobs': All possible options exhausted without Two remarks compared to the last script. -- The order of parameter types in the union is the order click will try to parse the value. \ No newline at end of file +- The order of parameter types in the union is the order click will try to parse the value. diff --git a/docs/usage/network.md b/docs/usage/network.md index 77b54e6..8e0a7c2 100644 --- a/docs/usage/network.md +++ b/docs/usage/network.md @@ -11,7 +11,7 @@ if __name__ == '__main__': !!! note For windows users, instead of using simple quotes for the following examples related to list parameters, you should use double quotes. - + ## IP_ADDRESS Converts string to an `ipaddress.IPv4Address` or `ipaddress.IPv6Address` object. @@ -110,7 +110,7 @@ max_ip = IPv4Address('127.0.0.125') def cli(first_ip, second_ip): click.echo(first_ip) click.echo(second_ip) -```` +```` ````bash $ python cli.py -f 127.0.0.1 -s 127.0.0.130 @@ -194,7 +194,7 @@ max_ip = IPv6Address('2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe') def cli(first_ip, second_ip): click.echo(first_ip) click.echo(second_ip) -```` +```` ````bash $ python cli.py -f 2001:db00::2 -s 2001:dc00::9 @@ -388,4 +388,4 @@ Your list of ip networks: $ python cli.py --networks='192.168.1.0/24 2001:db8:1234::/48 2001:db00::/24' Error: These items are not ip networks: ['192.168.1.0/24'] -```` \ No newline at end of file +```` diff --git a/docs/usage/numeric.md b/docs/usage/numeric.md index 985eca2..0de24a0 100644 --- a/docs/usage/numeric.md +++ b/docs/usage/numeric.md @@ -296,4 +296,4 @@ The floating point values you entered are: $ python cli.py --floats='1 1/2 -2.1' Error: These items are not floating point values: ['1/2'] -```` \ No newline at end of file +```` diff --git a/mkdocs.yml b/mkdocs.yml index f9aea44..c8e1f2d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,17 @@ edit_uri: tree/master/docs theme: name: material + palette: + - scheme: default + toggle: + icon: material/lightbulb-outline + name: Switch to dark mode + - scheme: slate + primary: teal + accent: teal + toggle: + icon: material/lightbulb + name: Switch to light mode nav: - Home: index.md diff --git a/noxfile.py b/noxfile.py index 96ed4cb..6690620 100644 --- a/noxfile.py +++ b/noxfile.py @@ -13,11 +13,19 @@ def lint(session): """Performs pep8 and security checks.""" source_code = 'click_params' - session.install('flake8==3.9.2', 'bandit==1.7.0') + session.install('flake8==3.9.2', 'bandit==1.7.4') session.run('flake8', source_code) session.run('bandit', '-r', source_code) +@nox.session(python=PYTHON_VERSIONS[-1]) +def safety(session): + """Checks vulnerabilities of the installed packages.""" + session.install('poetry>=1.0.0,<2.0.0') + session.run('poetry', 'install') + session.run('safety', 'check') + + @nox.session(python=PYTHON_VERSIONS) def tests(session): """Runs the test suite.""" @@ -25,15 +33,11 @@ def tests(session): session.run('poetry', 'install') session.run('pytest') - # we notify codecov when the latest version of python is used - if session.python == PYTHON_VERSIONS[-1] and CI_ENVIRONMENT: - session.run('codecov', '-f', 'coverage.xml') - @nox.session(python=PYTHON_VERSIONS[-1]) def docs(session): """Builds the documentation.""" - session.install('mkdocs==1.0.4') + session.install('mkdocs==1.3.0') session.run('mkdocs', 'build', '--clean') diff --git a/poetry.lock b/poetry.lock index 9e90357..1e5dd8e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,20 +1,20 @@ [[package]] name = "argcomplete" -version = "1.12.3" +version = "2.0.0" description = "Bash tab completion for argparse" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] -importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.6\" or python_version == \"3.7\""} +importlib-metadata = {version = ">=0.23,<5", markers = "python_version == \"3.7\""} [package.extras] -test = ["coverage", "flake8", "pexpect", "wheel"] +test = ["wheel", "pexpect", "flake8", "coverage"] [[package]] name = "atomicwrites" -version = "1.4.0" +version = "1.4.1" description = "Atomic file writes." category = "dev" optional = false @@ -22,25 +22,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.4.0" +version = "22.1.0" description = "Classes Without Boilerplate" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.5" [package.extras] -dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] -tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] -tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] [[package]] name = "bandit" -version = "1.7.1" +version = "1.7.4" description = "Security oriented static analyser for python code." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} @@ -48,52 +48,76 @@ GitPython = ">=1.0.1" PyYAML = ">=5.3.1" stevedore = ">=1.20.0" +[package.extras] +test = ["coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "toml", "beautifulsoup4 (>=4.8.0)", "pylint (==1.9.4)"] +toml = ["toml"] +yaml = ["pyyaml"] + +[[package]] +name = "black" +version = "22.6.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" -version = "2021.10.8" +version = "2022.6.15" description = "Python package for providing Mozilla's CA Bundle." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" + +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" [[package]] name = "charset-normalizer" -version = "2.0.12" +version = "2.1.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "dev" optional = false -python-versions = ">=3.5.0" +python-versions = ">=3.6.0" [package.extras] unicode_backport = ["unicodedata2"] [[package]] name = "click" -version = "8.0.4" +version = "8.1.3" description = "Composable command line interface toolkit" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] colorama = {version = "*", markers = "platform_system == \"Windows\""} importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} -[[package]] -name = "codecov" -version = "2.1.12" -description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab" -category = "dev" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" - -[package.dependencies] -coverage = "*" -requests = ">=2.7.9" - [[package]] name = "colorama" -version = "0.4.4" +version = "0.4.5" description = "Cross-platform colored terminal text." category = "main" optional = false @@ -101,22 +125,25 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "colorlog" -version = "4.8.0" -description = "Log formatting with colors!" +version = "6.6.0" +description = "Add colours to the output of Python's logging module." category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} +[package.extras] +development = ["black", "flake8", "mypy", "pytest", "types-colorama"] + [[package]] name = "coverage" -version = "6.2" +version = "6.4.3" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] toml = ["tomli"] @@ -131,23 +158,39 @@ python-versions = ">=3.5" [[package]] name = "distlib" -version = "0.3.4" +version = "0.3.5" description = "Distribution utilities" category = "dev" optional = false python-versions = "*" +[[package]] +name = "dparse" +version = "0.5.2" +description = "A parser for Python dependency files" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +packaging = "*" +toml = "*" + +[package.extras] +pipenv = ["pipenv"] +conda = ["pyyaml"] + [[package]] name = "filelock" -version = "3.4.1" +version = "3.8.0" description = "A platform independent file lock." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["furo (>=2021.8.17b43)", "sphinx (>=4.1)", "sphinx-autodoc-typehints (>=1.12)"] -testing = ["covdefaults (>=1.2.0)", "coverage (>=4)", "pytest (>=4)", "pytest-cov", "pytest-timeout (>=1.4.2)"] +testing = ["pytest-timeout (>=2.1)", "pytest-cov (>=3)", "pytest (>=7.1.2)", "coverage (>=6.4.2)", "covdefaults (>=2.2)"] +docs = ["sphinx-autodoc-typehints (>=1.19.1)", "sphinx (>=5.1.1)", "furo (>=2022.6.21)"] [[package]] name = "flake8" @@ -165,7 +208,7 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "ghp-import" -version = "2.0.2" +version = "2.1.0" description = "Copy your docs directly to the gh-pages branch." category = "dev" optional = false @@ -190,15 +233,26 @@ smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.20" -description = "Python Git Library" +version = "3.1.27" +description = "GitPython is a python library used to interact with Git repositories" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.8\""} + +[[package]] +name = "identify" +version = "2.5.3" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +license = ["ukkonen"] [[package]] name = "idna" @@ -210,35 +264,20 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "4.8.3" +version = "4.12.0" description = "Read metadata from Python packages" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] 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", "pep517", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] - -[[package]] -name = "importlib-resources" -version = "5.4.0" -description = "Read resources from Python packages" -category = "dev" -optional = false -python-versions = ">=3.6" - -[package.dependencies] -zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} - -[package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" @@ -248,13 +287,27 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "isort" +version = "5.10.1" +description = "A Python utility / library to sort Python imports." +category = "dev" +optional = false +python-versions = ">=3.6.1,<4.0" + +[package.extras] +pipfile_deprecated_finder = ["pipreqs", "requirementslib"] +requirements_deprecated_finder = ["pipreqs", "pip-api"] +colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] + [[package]] name = "jinja2" -version = "3.0.3" +version = "3.1.2" description = "A very fast and expressive template engine." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] MarkupSafe = ">=2.0" @@ -264,25 +317,25 @@ i18n = ["Babel (>=2.7)"] [[package]] name = "markdown" -version = "3.3.6" +version = "3.4.1" description = "Python implementation of Markdown." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""} [package.extras] -testing = ["coverage", "pyyaml"] +testing = ["pyyaml", "coverage"] [[package]] name = "markupsafe" -version = "2.0.1" +version = "2.1.1" description = "Safely add untrusted strings to HTML/XML markup." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "mccabe" @@ -302,7 +355,7 @@ python-versions = ">=3.6" [[package]] name = "mkdocs" -version = "1.2.3" +version = "1.3.0" description = "Project documentation with Markdown." category = "dev" optional = false @@ -311,8 +364,8 @@ python-versions = ">=3.6" [package.dependencies] click = ">=3.3" ghp-import = ">=1.0" -importlib-metadata = ">=3.10" -Jinja2 = ">=2.10.1" +importlib-metadata = ">=4.3" +Jinja2 = ">=2.10.2" Markdown = ">=3.2.1" mergedeep = ">=1.3.4" packaging = ">=20.5" @@ -325,19 +378,19 @@ i18n = ["babel (>=2.9.0)"] [[package]] name = "mkdocs-material" -version = "7.3.6" -description = "A Material Design theme for MkDocs" +version = "8.4.0" +description = "Documentation that simply works" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.7" [package.dependencies] -jinja2 = ">=2.11.1" +jinja2 = ">=3.0.2" markdown = ">=3.2" -mkdocs = ">=1.2.3" -mkdocs-material-extensions = ">=1.0" -pygments = ">=2.10" -pymdown-extensions = ">=9.0" +mkdocs = ">=1.3.0" +mkdocs-material-extensions = ">=1.0.3" +pygments = ">=2.12" +pymdown-extensions = ">=9.4" [[package]] name = "mkdocs-material-extensions" @@ -347,19 +400,38 @@ category = "dev" optional = false python-versions = ">=3.6" +[[package]] +name = "mypy-extensions" +version = "0.4.3" +description = "Experimental type system extensions for programs checked with the mypy typechecker." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "nodeenv" +version = "1.7.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" + [[package]] name = "nox" -version = "2019.11.9" +version = "2022.8.7" description = "Flexible test automation." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.7" [package.dependencies] -argcomplete = ">=1.9.4,<2.0" -colorlog = ">=2.6.1,<5.0.0" -py = ">=1.4.0,<2.0.0" -virtualenv = ">=14.0.0" +argcomplete = ">=1.9.4,<3.0" +colorlog = ">=2.6.1,<7.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +packaging = ">=20.9" +py = ">=1.4,<2.0.0" +typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} +virtualenv = ">=14" [package.extras] tox_to_nox = ["jinja2", "tox"] @@ -375,9 +447,17 @@ python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" +[[package]] +name = "pathspec" +version = "0.9.0" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" + [[package]] name = "pbr" -version = "5.8.1" +version = "5.10.0" description = "Python Build Reasonableness" category = "dev" optional = false @@ -385,15 +465,15 @@ python-versions = ">=2.6" [[package]] name = "platformdirs" -version = "2.4.0" +version = "2.5.2" description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." category = "dev" optional = false -python-versions = ">=3.6" +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)"] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] [[package]] name = "pluggy" @@ -410,6 +490,23 @@ importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pre-commit" +version = "2.20.0" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +toml = "*" +virtualenv = ">=20.0.8" + [[package]] name = "py" version = "1.11.0" @@ -436,41 +533,41 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pygments" -version = "2.11.2" +version = "2.12.0" description = "Pygments is a syntax highlighting package written in Python." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "pymdown-extensions" -version = "9.1" +version = "9.5" description = "Extension pack for Python Markdown." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] -Markdown = ">=3.2" +markdown = ">=3.2" [[package]] name = "pyparsing" -version = "3.0.7" -description = "Python parsing module" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.8" [package.extras] -diagrams = ["jinja2", "railroad-diagrams"] +diagrams = ["railroad-diagrams", "jinja2"] [[package]] name = "pytest" -version = "7.0.1" +version = "7.1.2" description = "pytest: simple powerful testing with Python" category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.dependencies] atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} @@ -548,21 +645,59 @@ pyyaml = "*" [[package]] name = "requests" -version = "2.27.1" +version = "2.28.1" description = "Python HTTP for Humans." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" +python-versions = ">=3.7, <4" [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} -idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" urllib3 = ">=1.21.1,<1.27" [package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] -use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "ruamel.yaml" +version = "0.17.21" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "dev" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "safety" +version = "2.1.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.5.1" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" [[package]] name = "six" @@ -602,30 +737,38 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "tomli" -version = "1.2.3" +version = "2.0.1" description = "A lil' TOML parser" category = "dev" optional = false +python-versions = ">=3.7" + +[[package]] +name = "typed-ast" +version = "1.5.4" +description = "a fork of Python 2 and 3 ast modules with type comment support" +category = "dev" +optional = false python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "4.1.1" -description = "Backported and Experimental Type Hints for Python 3.6+" +version = "4.3.0" +description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [[package]] name = "urllib3" -version = "1.26.8" +version = "1.26.11" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" [package.extras] -brotli = ["brotlipy (>=0.6.0)"] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] @@ -646,27 +789,25 @@ test = ["pytest (>=2.2.3)", "flake8 (>=2.4.0)", "isort (>=4.2.2)"] [[package]] name = "virtualenv" -version = "20.13.2" +version = "20.16.3" description = "Virtual Python Environment builder" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] -distlib = ">=0.3.1,<1" -filelock = ">=3.2,<4" -importlib-metadata = {version = ">=0.12", markers = "python_version < \"3.8\""} -importlib-resources = {version = ">=1.0", markers = "python_version < \"3.7\""} -platformdirs = ">=2,<3" -six = ">=1.9.0,<2" +distlib = ">=0.3.5,<1" +filelock = ">=3.4.1,<4" +importlib-metadata = {version = ">=4.8.3", markers = "python_version < \"3.8\""} +platformdirs = ">=2.4,<3" [package.extras] -docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] -testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] +docs = ["proselint (>=0.13)", "sphinx (>=5.1.1)", "sphinx-argparse (>=0.3.1)", "sphinx-rtd-theme (>=1)", "towncrier (>=21.9)"] +testing = ["coverage (>=6.2)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=21.3)", "pytest (>=7.0.1)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.2)", "pytest-mock (>=3.6.1)", "pytest-randomly (>=3.10.3)", "pytest-timeout (>=2.1)"] [[package]] name = "watchdog" -version = "2.1.6" +version = "2.1.9" description = "Filesystem events monitoring" category = "dev" optional = false @@ -677,199 +818,129 @@ watchmedo = ["PyYAML (>=3.10)"] [[package]] name = "zipp" -version = "3.6.0" +version = "3.8.1" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +docs = ["sphinx", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "2215f666a3158ecf5db32d00c6bb7d4e0a4438040cb9588d14368e8834a296e0" +python-versions = "^3.7" +content-hash = "58354a259ec1d2209f4bf66b9bd2d40b653cc90d27aff00a3866c8b11a962a15" [metadata.files] -argcomplete = [ - {file = "argcomplete-1.12.3-py2.py3-none-any.whl", hash = "sha256:291f0beca7fd49ce285d2f10e4c1c77e9460cf823eef2de54df0c0fec88b0d81"}, - {file = "argcomplete-1.12.3.tar.gz", hash = "sha256:2c7dbffd8c045ea534921e63b0be6fe65e88599990d8dc408ac8c542b72a5445"}, -] -atomicwrites = [ - {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, - {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, -] -attrs = [ - {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, - {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, -] +argcomplete = [] +atomicwrites = [] +attrs = [] bandit = [ - {file = "bandit-1.7.1-py3-none-any.whl", hash = "sha256:f5acd838e59c038a159b5c621cf0f8270b279e884eadd7b782d7491c02add0d4"}, - {file = "bandit-1.7.1.tar.gz", hash = "sha256:a81b00b5436e6880fa8ad6799bc830e02032047713cbb143a12939ac67eb756c"}, + {file = "bandit-1.7.4-py3-none-any.whl", hash = "sha256:412d3f259dab4077d0e7f0c11f50f650cc7d10db905d98f6520a95a18049658a"}, + {file = "bandit-1.7.4.tar.gz", hash = "sha256:2d63a8c573417bae338962d4b9b06fbc6080f74ecd955a092849e1e65c717bd2"}, ] -certifi = [ - {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, - {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, -] -charset-normalizer = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, +black = [] +certifi = [] +cfgv = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, ] +charset-normalizer = [] click = [ - {file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"}, - {file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"}, -] -codecov = [ - {file = "codecov-2.1.12-py2.py3-none-any.whl", hash = "sha256:585dc217dc3d8185198ceb402f85d5cb5dbfa0c5f350a5abcdf9e347776a5b47"}, - {file = "codecov-2.1.12-py3.8.egg", hash = "sha256:782a8e5352f22593cbc5427a35320b99490eb24d9dcfa2155fd99d2b75cfb635"}, - {file = "codecov-2.1.12.tar.gz", hash = "sha256:a0da46bb5025426da895af90938def8ee12d37fcbcbbbc15b6dc64cf7ebc51c1"}, -] -colorama = [ - {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, - {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, ] +colorama = [] colorlog = [ - {file = "colorlog-4.8.0-py2.py3-none-any.whl", hash = "sha256:3dd15cb27e8119a24c1a7b5c93f9f3b455855e0f73993b1c25921b2f646f1dcd"}, - {file = "colorlog-4.8.0.tar.gz", hash = "sha256:59b53160c60902c405cdec28d38356e09d40686659048893e026ecbd589516b1"}, -] -coverage = [ - {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, - {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, - {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, - {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, - {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, - {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, - {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, - {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, - {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, - {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, - {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, - {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, - {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, - {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, - {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, - {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, - {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, - {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, - {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, - {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, - {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, - {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, - {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, - {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, - {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, - {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, - {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, - {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, - {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, + {file = "colorlog-6.6.0-py2.py3-none-any.whl", hash = "sha256:351c51e866c86c3217f08e4b067a7974a678be78f07f85fc2d55b8babde6d94e"}, + {file = "colorlog-6.6.0.tar.gz", hash = "sha256:344f73204009e4c83c5b6beb00b3c45dc70fcdae3c80db919e0a4171d006fde8"}, ] +coverage = [] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] -distlib = [ - {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, - {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, -] -filelock = [ - {file = "filelock-3.4.1-py3-none-any.whl", hash = "sha256:a4bc51381e01502a30e9f06dd4fa19a1712eab852b6fb0f84fd7cce0793d8ca3"}, - {file = "filelock-3.4.1.tar.gz", hash = "sha256:0f12f552b42b5bf60dba233710bf71337d35494fc8bdd4fd6d9f6d082ad45e06"}, -] +distlib = [] +dparse = [] +filelock = [] flake8 = [ {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] ghp-import = [ - {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, - {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, + {file = "ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343"}, + {file = "ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619"}, ] gitdb = [ {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] gitpython = [ - {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, - {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, + {file = "GitPython-3.1.27-py3-none-any.whl", hash = "sha256:5b68b000463593e05ff2b261acff0ff0972df8ab1b70d3cdbd41b546c8b8fc3d"}, + {file = "GitPython-3.1.27.tar.gz", hash = "sha256:1c885ce809e8ba2d88a29befeb385fcea06338d3640712b59ca623c220bb5704"}, ] +identify = [] 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.8.3-py3-none-any.whl", hash = "sha256:65a9576a5b2d58ca44d133c42a241905cc45e34d2c06fd5ba2bafa221e5d7b5e"}, - {file = "importlib_metadata-4.8.3.tar.gz", hash = "sha256:766abffff765960fcc18003801f7044eb6755ffae4521c8e8ce8e83b9c9b0668"}, -] -importlib-resources = [ - {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, - {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, -] +importlib-metadata = [] iniconfig = [ {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] -jinja2 = [ - {file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"}, - {file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"}, +isort = [ + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] -markdown = [ - {file = "Markdown-3.3.6-py3-none-any.whl", hash = "sha256:9923332318f843411e9932237530df53162e29dc7a4e2b91e35764583c46c9a3"}, - {file = "Markdown-3.3.6.tar.gz", hash = "sha256:76df8ae32294ec39dcf89340382882dfa12975f87f45c3ed1ecdb1e8cefc7006"}, +jinja2 = [ + {file = "Jinja2-3.1.2-py3-none-any.whl", hash = "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61"}, + {file = "Jinja2-3.1.2.tar.gz", hash = "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852"}, ] +markdown = [] markupsafe = [ - {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"}, - {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"}, - {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"}, - {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"}, - {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"}, - {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"}, + {file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"}, + {file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"}, + {file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"}, + {file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"}, + {file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -880,37 +951,38 @@ mergedeep = [ {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, ] mkdocs = [ - {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, - {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, -] -mkdocs-material = [ - {file = "mkdocs-material-7.3.6.tar.gz", hash = "sha256:1b1dbd8ef2508b358d93af55a5c5db3f141c95667fad802301ec621c40c7c217"}, - {file = "mkdocs_material-7.3.6-py2.py3-none-any.whl", hash = "sha256:1b6b3e9e09f922c2d7f1160fe15c8f43d4adc0d6fb81aa6ff0cbc7ef5b78ec75"}, + {file = "mkdocs-1.3.0-py3-none-any.whl", hash = "sha256:26bd2b03d739ac57a3e6eed0b7bcc86168703b719c27b99ad6ca91dc439aacde"}, + {file = "mkdocs-1.3.0.tar.gz", hash = "sha256:b504405b04da38795fec9b2e5e28f6aa3a73bb0960cb6d5d27ead28952bd35ea"}, ] +mkdocs-material = [] mkdocs-material-extensions = [ {file = "mkdocs-material-extensions-1.0.3.tar.gz", hash = "sha256:bfd24dfdef7b41c312ede42648f9eb83476ea168ec163b613f9abd12bbfddba2"}, {file = "mkdocs_material_extensions-1.0.3-py3-none-any.whl", hash = "sha256:a82b70e533ce060b2a5d9eb2bc2e1be201cf61f901f93704b4acf6e3d5983a44"}, ] -nox = [ - {file = "nox-2019.11.9-py2.py3-none-any.whl", hash = "sha256:0f4b489fdd0eb5665f8c5ee89e5aeb648beae6ccbb363b2492a6786f26e70d85"}, - {file = "nox-2019.11.9.tar.gz", hash = "sha256:22d0f45ad2bd2d75fa4a243d8d8b84359dbf43134ec5cbff4de9f243b6d528b8"}, +mypy-extensions = [ + {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, + {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] +nodeenv = [] +nox = [] packaging = [ {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] -pbr = [ - {file = "pbr-5.8.1-py2.py3-none-any.whl", hash = "sha256:27108648368782d07bbf1cb468ad2e2eeef29086affd14087a6d04b7de8af4ec"}, - {file = "pbr-5.8.1.tar.gz", hash = "sha256:66bc5a34912f408bb3925bf21231cb6f59206267b7f63f3503ef865c1a292e25"}, +pathspec = [ + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] +pbr = [] platformdirs = [ - {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, - {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, ] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, ] +pre-commit = [] py = [ {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, @@ -924,20 +996,20 @@ pyflakes = [ {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pygments = [ - {file = "Pygments-2.11.2-py3-none-any.whl", hash = "sha256:44238f1b60a76d78fc8ca0528ee429702aae011c265fe6a8dd8b63049ae41c65"}, - {file = "Pygments-2.11.2.tar.gz", hash = "sha256:4e426f72023d88d03b2fa258de560726ce890ff3b630f88c21cbb8b2503b8c6a"}, + {file = "Pygments-2.12.0-py3-none-any.whl", hash = "sha256:dc9c10fb40944260f6ed4c688ece0cd2048414940f1cea51b8b226318411c519"}, + {file = "Pygments-2.12.0.tar.gz", hash = "sha256:5eb116118f9612ff1ee89ac96437bb6b49e8f04d8a13b514ba26f620208e26eb"}, ] pymdown-extensions = [ - {file = "pymdown-extensions-9.1.tar.gz", hash = "sha256:74247f2c80f1d9e3c7242abe1c16317da36c6f26c7ad4b8a7f457f0ec20f0365"}, - {file = "pymdown_extensions-9.1-py3-none-any.whl", hash = "sha256:b03e66f91f33af4a6e7a0e20c740313522995f69a03d86316b1449766c473d0e"}, + {file = "pymdown_extensions-9.5-py3-none-any.whl", hash = "sha256:ec141c0f4983755349f0c8710416348d1a13753976c028186ed14f190c8061c4"}, + {file = "pymdown_extensions-9.5.tar.gz", hash = "sha256:3ef2d998c0d5fa7eb09291926d90d69391283561cf6306f85cd588a5eb5befa0"}, ] pyparsing = [ - {file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"}, - {file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"}, + {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, + {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, ] pytest = [ - {file = "pytest-7.0.1-py3-none-any.whl", hash = "sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db"}, - {file = "pytest-7.0.1.tar.gz", hash = "sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171"}, + {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, + {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, ] pytest-cov = [ {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, @@ -990,10 +1062,10 @@ pyyaml-env-tag = [ {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, ] -requests = [ - {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, - {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, -] +requests = [] +"ruamel.yaml" = [] +"ruamel.yaml.clib" = [] +safety = [] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, @@ -1011,51 +1083,41 @@ toml = [ {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] tomli = [ - {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, - {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, + {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, + {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -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"}, -] -urllib3 = [ - {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, - {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, +typed-ast = [ + {file = "typed_ast-1.5.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:669dd0c4167f6f2cd9f57041e03c3c2ebf9063d0757dc89f79ba1daa2bfca9d4"}, + {file = "typed_ast-1.5.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:211260621ab1cd7324e0798d6be953d00b74e0428382991adfddb352252f1d62"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:267e3f78697a6c00c689c03db4876dd1efdfea2f251a5ad6555e82a26847b4ac"}, + {file = "typed_ast-1.5.4-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c542eeda69212fa10a7ada75e668876fdec5f856cd3d06829e6aa64ad17c8dfe"}, + {file = "typed_ast-1.5.4-cp310-cp310-win_amd64.whl", hash = "sha256:a9916d2bb8865f973824fb47436fa45e1ebf2efd920f2b9f99342cb7fab93f72"}, + {file = "typed_ast-1.5.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:79b1e0869db7c830ba6a981d58711c88b6677506e648496b1f64ac7d15633aec"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a94d55d142c9265f4ea46fab70977a1944ecae359ae867397757d836ea5a3f47"}, + {file = "typed_ast-1.5.4-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:183afdf0ec5b1b211724dfef3d2cad2d767cbefac291f24d69b00546c1837fb6"}, + {file = "typed_ast-1.5.4-cp36-cp36m-win_amd64.whl", hash = "sha256:639c5f0b21776605dd6c9dbe592d5228f021404dafd377e2b7ac046b0349b1a1"}, + {file = "typed_ast-1.5.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:cf4afcfac006ece570e32d6fa90ab74a17245b83dfd6655a6f68568098345ff6"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed855bbe3eb3715fca349c80174cfcfd699c2f9de574d40527b8429acae23a66"}, + {file = "typed_ast-1.5.4-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6778e1b2f81dfc7bc58e4b259363b83d2e509a65198e85d5700dfae4c6c8ff1c"}, + {file = "typed_ast-1.5.4-cp37-cp37m-win_amd64.whl", hash = "sha256:0261195c2062caf107831e92a76764c81227dae162c4f75192c0d489faf751a2"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2efae9db7a8c05ad5547d522e7dbe62c83d838d3906a3716d1478b6c1d61388d"}, + {file = "typed_ast-1.5.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7d5d014b7daa8b0bf2eaef684295acae12b036d79f54178b92a2b6a56f92278f"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:370788a63915e82fd6f212865a596a0fefcbb7d408bbbb13dea723d971ed8bdc"}, + {file = "typed_ast-1.5.4-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e964b4ff86550a7a7d56345c7864b18f403f5bd7380edf44a3c1fb4ee7ac6c6"}, + {file = "typed_ast-1.5.4-cp38-cp38-win_amd64.whl", hash = "sha256:683407d92dc953c8a7347119596f0b0e6c55eb98ebebd9b23437501b28dcbb8e"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4879da6c9b73443f97e731b617184a596ac1235fe91f98d279a7af36c796da35"}, + {file = "typed_ast-1.5.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3e123d878ba170397916557d31c8f589951e353cc95fb7f24f6bb69adc1a8a97"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebd9d7f80ccf7a82ac5f88c521115cc55d84e35bf8b446fcd7836eb6b98929a3"}, + {file = "typed_ast-1.5.4-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:98f80dee3c03455e92796b58b98ff6ca0b2a6f652120c263efdba4d6c5e58f72"}, + {file = "typed_ast-1.5.4-cp39-cp39-win_amd64.whl", hash = "sha256:0fdbcf2fef0ca421a3f5912555804296f0b0960f0418c440f5d6d3abb549f3e1"}, + {file = "typed_ast-1.5.4.tar.gz", hash = "sha256:39e21ceb7388e4bb37f4c679d72707ed46c2fbf2a5609b8b8ebc4b067d977df2"}, ] +typing-extensions = [] +urllib3 = [] validators = [ {file = "validators-0.18.2-py3-none-any.whl", hash = "sha256:0143dcca8a386498edaf5780cbd5960da1a4c85e0719f3ee5c9b41249c4fefbd"}, {file = "validators-0.18.2.tar.gz", hash = "sha256:37cd9a9213278538ad09b5b9f9134266e7c226ab1fede1d500e29e0a8fbb9ea6"}, ] -virtualenv = [ - {file = "virtualenv-20.13.2-py2.py3-none-any.whl", hash = "sha256:e7b34c9474e6476ee208c43a4d9ac1510b041c68347eabfe9a9ea0c86aa0a46b"}, - {file = "virtualenv-20.13.2.tar.gz", hash = "sha256:01f5f80744d24a3743ce61858123488e91cb2dd1d3bdf92adaf1bba39ffdedf0"}, -] -watchdog = [ - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, - {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, - {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, - {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, - {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, - {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, - {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, - {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, - {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, - {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, - {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, - {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, -] -zipp = [ - {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, - {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, -] +virtualenv = [] +watchdog = [] +zipp = [] diff --git a/pyproject.toml b/pyproject.toml index 4b8db5e..d77db73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,9 +36,24 @@ pytest-cov = "^2.8.1" pytest-mock = "^2.0.0" bandit = "^1.6.2" flake8 = "^3.7.9" -nox = "^2019.11.9" -codecov = "^2.1.11" -mkdocs-material = "^7.1.4" +nox = "^2022.8.7" +mkdocs-material = "^8.4.0" +pre-commit = "^2.18.1" +isort = "^5.10.1" +black = "^22.3.0" +safety = "^2.1.1" + +[tool.pytest.ini_options] +testpaths = ["tests"] +addopts = "--cov=click_params --cov-report html --cov-report xml" + +[tool.isort] +line_length = 120 +profile = "black" + +[tool.black] +line-length = 120 +skip-string-normalization = true [build-system] requires = ["poetry>=0.12"] diff --git a/setup.cfg b/setup.cfg index d14657e..9b2f7e5 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,3 @@ -[tool:pytest] -testpaths = tests -addopts = --cov=click_params --cov-report html --cov-report xml - [flake8] max-line-length = 120 exclude = @@ -10,4 +6,4 @@ exclude = htmlcov, tests, .pytest_cache, - .coverage \ No newline at end of file + .coverage diff --git a/tests/conftest.py b/tests/conftest.py index 7b6d57a..b55ea0c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,5 +1,5 @@ -from click.testing import CliRunner import pytest +from click.testing import CliRunner @pytest.fixture() diff --git a/tests/test_base.py b/tests/test_base.py index ae8c064..fa14b3b 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -4,12 +4,13 @@ import pytest from validators.utils import validator -from click_params.base import RangeParamType, BaseParamType, ValidatorParamType, ListParamType -from click_params.numeric import DECIMAL, FRACTION, COMPLEX +from click_params.base import BaseParamType, ListParamType, RangeParamType, ValidatorParamType +from click_params.numeric import COMPLEX, DECIMAL, FRACTION class IntType(BaseParamType): """We use this custom type to test BaseParamType""" + name = 'integer' def __init__(self): @@ -74,6 +75,7 @@ def test_should_return_value_when_giving_corrected_value(self, value): class IntRange(RangeParamType): """This class will be used to test the correctness of RangeParamType""" + name = 'int range' def __init__(self, minimum: int = None, maximum: int = None, clamp: bool = False): @@ -88,33 +90,30 @@ def test_class_representation_is_correct(self): # we test clamp parameter usage - @pytest.mark.parametrize(('minimum', 'maximum', 'given_input', 'expected_value'), [ - (None, 5, '-1', -1), - (None, 5, '8', 5), - (5, 10, '8', 8), - (5, 10, '2', 5), - (5, None, '8', 8) - ]) - def test_should_return_correct_value_when_setting_clamp_to_true(self, minimum, maximum, given_input, - expected_value): + @pytest.mark.parametrize( + ('minimum', 'maximum', 'given_input', 'expected_value'), + [(None, 5, '-1', -1), (None, 5, '8', 5), (5, 10, '8', 8), (5, 10, '2', 5), (5, None, '8', 8)], + ) + def test_should_return_correct_value_when_setting_clamp_to_true( + self, minimum, maximum, given_input, expected_value + ): int_range = IntRange(minimum, maximum, True) assert expected_value == int_range.convert(given_input, None, None) - @pytest.mark.parametrize(('minimum', 'maximum', 'value'), [ - (5, 10, '6'), - (5, 10, '5'), - (5, 10, '10') - ]) + @pytest.mark.parametrize(('minimum', 'maximum', 'value'), [(5, 10, '6'), (5, 10, '5'), (5, 10, '10')]) def test_should_return_correct_value_when_setting_clamp_to_false(self, minimum, maximum, value): int_range = IntRange(minimum, maximum) assert int(value) == int_range.convert(value, None, None) - @pytest.mark.parametrize(('minimum', 'maximum', 'given_input', 'message'), [ - (5, None, '4', '4 is smaller than the minimum valid value 5.'), - (None, 10, '11', '11 is bigger than the maximum valid value 10.'), - (5, 10, '4', '4 is not in the valid range of 5 to 10.'), - (5, 10, '11', '11 is not in the valid range of 5 to 10.') - ]) + @pytest.mark.parametrize( + ('minimum', 'maximum', 'given_input', 'message'), + [ + (5, None, '4', '4 is smaller than the minimum valid value 5.'), + (None, 10, '11', '11 is bigger than the maximum valid value 10.'), + (5, 10, '4', '4 is not in the valid range of 5 to 10.'), + (5, 10, '11', '11 is not in the valid range of 5 to 10.'), + ], + ) def test_should_raise_error_when_giving_values_outside_limits(self, minimum, maximum, given_input, message): with pytest.raises(click.BadParameter) as exc_info: int_range = IntRange(minimum, maximum, False) @@ -134,33 +133,37 @@ def test_should_raise_error_when_instantiating_with_non_string_parameter(self, s assert 'separator must be a string' == str(exc_info.value) - @pytest.mark.parametrize('separator', [ - {}, # default separator should be used i.e "," - {'separator': ' '}, - {'separator': ';'} - ]) + @pytest.mark.parametrize( + 'separator', [{}, {'separator': ' '}, {'separator': ';'}] # default separator should be used i.e "," + ) def test_should_not_raise_error_when_instantiating_with_a_string(self, separator): base_list = ListParamType(click.INT, **separator) assert not base_list._convert_called # we test method _strip_separator - @pytest.mark.parametrize(('separator', 'expression'), [ - (',', '1,2'), - (',', ',1,2,'), - (';', ';1;2'), - (' ', '1 2 '), - ]) + @pytest.mark.parametrize( + ('separator', 'expression'), + [ + (',', '1,2'), + (',', ',1,2,'), + (';', ';1;2'), + (' ', '1 2 '), + ], + ) def test_should_return_correct_expression(self, separator, expression): base_list = ListParamType(click.INT, separator) assert f'1{separator}2' == base_list._strip_separator(expression) - @pytest.mark.parametrize(('expression', 'param_type', 'name', 'errors'), [ - ('1,foo,2', click.INT, 'integers', ['foo']), - ('1.4,bar,2.8', click.FLOAT, 'floating point values', ['bar']), - ('1,.2,foo', DECIMAL, 'decimal values', ['foo']), - ('2,1/0', FRACTION, 'fraction values', ['1/0']), - ]) + @pytest.mark.parametrize( + ('expression', 'param_type', 'name', 'errors'), + [ + ('1,foo,2', click.INT, 'integers', ['foo']), + ('1.4,bar,2.8', click.FLOAT, 'floating point values', ['bar']), + ('1,.2,foo', DECIMAL, 'decimal values', ['foo']), + ('2,1/0', FRACTION, 'fraction values', ['1/0']), + ], + ) def test_should_raise_error_when_items_are_incorrect(self, expression, param_type, name, errors): base_list = ListParamType(param_type, name=name) @@ -169,12 +172,15 @@ def test_should_raise_error_when_items_are_incorrect(self, expression, param_typ assert f'These items are not {name}: {errors}' == str(exc_info.value) - @pytest.mark.parametrize(('expression', 'param_type', 'name', 'values'), [ - ('1,2,3', click.INT, 'integers', [1, 2, 3]), - ('1,2.5', click.FLOAT, 'floating point values', [1.0, 2.5]), - ('2', FRACTION, 'fraction values', [Fraction(2, 1)]), - ('5,1.4,2+1j', COMPLEX, 'complex values', [complex(5, 0), complex(1.4, 0), complex(2, 1)]) - ]) + @pytest.mark.parametrize( + ('expression', 'param_type', 'name', 'values'), + [ + ('1,2,3', click.INT, 'integers', [1, 2, 3]), + ('1,2.5', click.FLOAT, 'floating point values', [1.0, 2.5]), + ('2', FRACTION, 'fraction values', [Fraction(2, 1)]), + ('5,1.4,2+1j', COMPLEX, 'complex values', [complex(5, 0), complex(1.4, 0), complex(2, 1)]), + ], + ) def test_should_return_correct_items_when_giving_correct_expression(self, expression, param_type, name, values): # noinspection PyTypeChecker base_list = ListParamType(param_type, name=name) @@ -186,9 +192,7 @@ def test_should_return_correct_items_when_giving_correct_expression(self, expres assert base_list._convert_called assert values == base_list.convert(values, None, None) - @pytest.mark.parametrize('param_type', [ - click.INT, click.FLOAT, click.STRING, FRACTION, COMPLEX - ]) + @pytest.mark.parametrize('param_type', [click.INT, click.FLOAT, click.STRING, FRACTION, COMPLEX]) def test_should_return_empty_list_with_ignore_empty_string(self, param_type): base_list = ListParamType(param_type=param_type, ignore_empty=True) assert base_list.convert("", None, None) == [] diff --git a/tests/test_domain.py b/tests/test_domain.py index d7775b3..44bfe3c 100644 --- a/tests/test_domain.py +++ b/tests/test_domain.py @@ -2,45 +2,59 @@ import pytest from click_params.domain import ( - DOMAIN, PUBLIC_URL, URL, EMAIL, SLUG, DomainListParamType, PublicUrlListParamType, UrlListParamType, - EmailListParamType, SlugListParamType + DOMAIN, + EMAIL, + PUBLIC_URL, + SLUG, + URL, + DomainListParamType, + EmailListParamType, + PublicUrlListParamType, + SlugListParamType, + UrlListParamType, +) +from tests.helpers import assert_equals_output, assert_in_output + + +@pytest.mark.parametrize( + ('name', 'parameter'), + [ + ('domain name', DOMAIN), + ('url', PUBLIC_URL), + ('url', URL), + ('email address', EMAIL), + ('slug', SLUG), + ('domain name list', DomainListParamType()), + ('url list', UrlListParamType()), + ('url list', PublicUrlListParamType()), + ('email address list', EmailListParamType()), + ('slug list', SlugListParamType()), + ], ) -from tests.helpers import assert_in_output, assert_equals_output - - -@pytest.mark.parametrize(('name', 'parameter'), [ - ('domain name', DOMAIN), - ('url', PUBLIC_URL), - ('url', URL), - ('email address', EMAIL), - ('slug', SLUG), - ('domain name list', DomainListParamType()), - ('url list', UrlListParamType()), - ('url list', PublicUrlListParamType()), - ('email address list', EmailListParamType()), - ('slug list', SlugListParamType()), -]) def test_parameter_name_and_representation_are_correct(name, parameter): assert name == parameter.name assert name.upper() == repr(parameter) -@pytest.mark.parametrize(('parameter', 'param_value'), [ - (DOMAIN, 'foo'), - (DOMAIN, '4'), - # public url - (PUBLIC_URL, 'http://foo'), - (PUBLIC_URL, 'foo://bar.com'), - (PUBLIC_URL, 'http://10.0.0.1'), - # URL - (URL, 'http://foo'), - (URL, 'foo://bar.com'), - # email - (EMAIL, 'bogus@@'), - (EMAIL, 'bogus@foo'), - # slug - (SLUG, 'foo.bar') -]) +@pytest.mark.parametrize( + ('parameter', 'param_value'), + [ + (DOMAIN, 'foo'), + (DOMAIN, '4'), + # public url + (PUBLIC_URL, 'http://foo'), + (PUBLIC_URL, 'foo://bar.com'), + (PUBLIC_URL, 'http://10.0.0.1'), + # URL + (URL, 'http://foo'), + (URL, 'foo://bar.com'), + # email + (EMAIL, 'bogus@@'), + (EMAIL, 'bogus@foo'), + # slug + (SLUG, 'foo.bar'), + ], +) def test_should_print_error_when_giving_incorrect_option_for_simple_types(runner, parameter, param_value): @click.command() @click.option('-v', 'value', type=parameter) @@ -52,13 +66,16 @@ def cli(value): assert_in_output(2, f'{param_value} is not a valid {parameter.name}', result) -@pytest.mark.parametrize(('parameter', 'expression', 'message'), [ - (DomainListParamType(' '), 'foo.com bar', "domain names: ['bar']"), - (UrlListParamType(' '), 'http://foo.com foo://bar.com', "urls: ['foo://bar.com']"), - (PublicUrlListParamType(' '), 'http://foo.com ftp://10.0.0.1', "urls: ['ftp://10.0.0.1']"), - (EmailListParamType(' '), 'bar@yahoo.fr bogus@@ foo@gmail.com', "email addresses: ['bogus@@']"), - (SlugListParamType(' '), 'foo bar.com tar_foo', "slugs: ['bar.com']"), -]) +@pytest.mark.parametrize( + ('parameter', 'expression', 'message'), + [ + (DomainListParamType(' '), 'foo.com bar', "domain names: ['bar']"), + (UrlListParamType(' '), 'http://foo.com foo://bar.com', "urls: ['foo://bar.com']"), + (PublicUrlListParamType(' '), 'http://foo.com ftp://10.0.0.1', "urls: ['ftp://10.0.0.1']"), + (EmailListParamType(' '), 'bar@yahoo.fr bogus@@ foo@gmail.com', "email addresses: ['bogus@@']"), + (SlugListParamType(' '), 'foo bar.com tar_foo', "slugs: ['bar.com']"), + ], +) def test_should_print_error_when_giving_incorrect_option_for_list_types(runner, parameter, expression, message): @click.command() @click.option('-v', 'values', type=parameter) @@ -70,22 +87,25 @@ def cli(values): assert_in_output(2, f'These items are not {message}', result) -@pytest.mark.parametrize(('parameter', 'param_value'), [ - (DOMAIN, 'foo.com'), - # public url - (PUBLIC_URL, 'http://foo.com'), - (PUBLIC_URL, 'https://1.1.1.1/path'), - # url - (URL, 'ftp://bar.com'), - (URL, 'http://10.0.0.1'), - # email - (EMAIL, 'foo@bar.com'), - (EMAIL, 'bar@académie.fr'), - # slug - (SLUG, 'foo'), - (SLUG, 'foo-bar'), - (SLUG, 'foo-bar_tar') -]) +@pytest.mark.parametrize( + ('parameter', 'param_value'), + [ + (DOMAIN, 'foo.com'), + # public url + (PUBLIC_URL, 'http://foo.com'), + (PUBLIC_URL, 'https://1.1.1.1/path'), + # url + (URL, 'ftp://bar.com'), + (URL, 'http://10.0.0.1'), + # email + (EMAIL, 'foo@bar.com'), + (EMAIL, 'bar@académie.fr'), + # slug + (SLUG, 'foo'), + (SLUG, 'foo-bar'), + (SLUG, 'foo-bar_tar'), + ], +) def test_should_print_correct_output_when_giving_correct_option_for_simple_types(runner, parameter, param_value): @click.command() @click.option('-v', 'value', type=parameter) @@ -97,25 +117,29 @@ def cli(value): assert_equals_output(0, f'{param_value}\n', result) -@pytest.mark.parametrize(('parameter', 'expression', 'expected_output'), [ - # domain list - (DomainListParamType(), 'foo.com,bar.fr', "['foo.com', 'bar.fr']\n"), - (DomainListParamType(' '), 'foo.com bar.fr', "['foo.com', 'bar.fr']\n"), - # url list - (UrlListParamType(), 'https://foo.com,ftp://bar.fr', "['https://foo.com', 'ftp://bar.fr']\n"), - (UrlListParamType(' '), 'https://10.0.0.1 ftp://bar.fr', "['https://10.0.0.1', 'ftp://bar.fr']\n"), - # public url list - (PublicUrlListParamType(), 'https://foo.com,ftp://bar.fr', "['https://foo.com', 'ftp://bar.fr']\n"), - (PublicUrlListParamType(' '), 'https://foo.com ftp://1.1.1.1', "['https://foo.com', 'ftp://1.1.1.1']\n"), - # email list - (EmailListParamType(), 'bar@académie.fr,foo@gmail.com', "['bar@académie.fr', 'foo@gmail.com']\n"), - (EmailListParamType(' '), 'bar@académie.fr foo@gmail.com', "['bar@académie.fr', 'foo@gmail.com']\n"), - # slug list - (SlugListParamType(), 'foo,bar_com,tar-1', "['foo', 'bar_com', 'tar-1']\n"), - (SlugListParamType(', '), 'foo, bar_com, tar-1', "['foo', 'bar_com', 'tar-1']\n"), -]) -def test_should_print_correct_output_when_giving_correct_option_for_list_types(runner, parameter, expression, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'expression', 'expected_output'), + [ + # domain list + (DomainListParamType(), 'foo.com,bar.fr', "['foo.com', 'bar.fr']\n"), + (DomainListParamType(' '), 'foo.com bar.fr', "['foo.com', 'bar.fr']\n"), + # url list + (UrlListParamType(), 'https://foo.com,ftp://bar.fr', "['https://foo.com', 'ftp://bar.fr']\n"), + (UrlListParamType(' '), 'https://10.0.0.1 ftp://bar.fr', "['https://10.0.0.1', 'ftp://bar.fr']\n"), + # public url list + (PublicUrlListParamType(), 'https://foo.com,ftp://bar.fr', "['https://foo.com', 'ftp://bar.fr']\n"), + (PublicUrlListParamType(' '), 'https://foo.com ftp://1.1.1.1', "['https://foo.com', 'ftp://1.1.1.1']\n"), + # email list + (EmailListParamType(), 'bar@académie.fr,foo@gmail.com', "['bar@académie.fr', 'foo@gmail.com']\n"), + (EmailListParamType(' '), 'bar@académie.fr foo@gmail.com', "['bar@académie.fr', 'foo@gmail.com']\n"), + # slug list + (SlugListParamType(), 'foo,bar_com,tar-1', "['foo', 'bar_com', 'tar-1']\n"), + (SlugListParamType(', '), 'foo, bar_com, tar-1', "['foo', 'bar_com', 'tar-1']\n"), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_list_types( + runner, parameter, expression, expected_output +): @click.command() @click.option('-v', 'values', type=parameter) def cli(values): @@ -126,9 +150,9 @@ def cli(values): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize("param_type", [ - DomainListParamType, PublicUrlListParamType, UrlListParamType, EmailListParamType, SlugListParamType -]) +@pytest.mark.parametrize( + "param_type", [DomainListParamType, PublicUrlListParamType, UrlListParamType, EmailListParamType, SlugListParamType] +) def test_domain_list_param_types_ignore_empty_string(param_type): domain_list_type = param_type(ignore_empty=True) diff --git a/tests/test_miscellaneous.py b/tests/test_miscellaneous.py index 05ec94b..4c4359d 100644 --- a/tests/test_miscellaneous.py +++ b/tests/test_miscellaneous.py @@ -4,33 +4,45 @@ import pytest from click_params.miscellaneous import ( - JSON, MAC_ADDRESS, JsonParamType, StringListParamType, MacAddressListParamType, UUIDListParamType, - DateTimeListParamType, FirstOf, ChoiceListParamType + JSON, + MAC_ADDRESS, + ChoiceListParamType, + DateTimeListParamType, + FirstOf, + JsonParamType, + MacAddressListParamType, + StringListParamType, + UUIDListParamType, +) +from tests.helpers import assert_equals_output, assert_in_output + + +@pytest.mark.parametrize( + ('parameter', 'name'), + [ + (JSON, 'json'), + (MAC_ADDRESS, 'mac address'), + (StringListParamType(), 'string list'), + (ChoiceListParamType(['a', 'b', 'c']), 'choice list'), + (MacAddressListParamType(), 'mac address list'), + (UUIDListParamType(), 'uuid list'), + (DateTimeListParamType(), 'datetime list'), + ], ) - -from tests.helpers import assert_in_output, assert_equals_output - - -@pytest.mark.parametrize(('parameter', 'name'), [ - (JSON, 'json'), - (MAC_ADDRESS, 'mac address'), - (StringListParamType(), 'string list'), - (ChoiceListParamType(['a', 'b', 'c']), 'choice list'), - (MacAddressListParamType(), 'mac address list'), - (UUIDListParamType(), 'uuid list'), - (DateTimeListParamType(), 'datetime list') -]) def test_parameter_name_and_representation_are_correct(parameter, name): assert name == parameter.name assert name.upper() == repr(parameter) -@pytest.mark.parametrize(('parameter', 'expression', 'message'), [ - (JSON, '2019-06-17', 'json string'), - (JSON, '2f', 'json string'), - (MAC_ADDRESS, '00:00:00:00:00', 'mac address'), - (MAC_ADDRESS, 'foo', 'mac address') -]) +@pytest.mark.parametrize( + ('parameter', 'expression', 'message'), + [ + (JSON, '2019-06-17', 'json string'), + (JSON, '2f', 'json string'), + (MAC_ADDRESS, '00:00:00:00:00', 'mac address'), + (MAC_ADDRESS, 'foo', 'mac address'), + ], +) def test_should_print_error_when_giving_incorrect_option_for_simple_types(runner, parameter, expression, message): @click.command() @click.option('-v', 'value', type=parameter) @@ -42,11 +54,18 @@ def cli(value): assert_in_output(2, f'{expression} is not a valid {message}', result) -@pytest.mark.parametrize(('parameter', 'expression', 'message'), [ - (MacAddressListParamType(' '), 'D4:6A:6A:12:B0:75 foo 00:00:00:00:00', "mac addresses: ['foo', '00:00:00:00:00']"), - (UUIDListParamType(' '), 'foo a7309d0b-c858-4d54-b6e1-1c20f8c22047 142-48dr', "uuid: ['foo', '142-48dr']"), - (DateTimeListParamType(' '), '145 2019-01-01 2019/01/01', "datetimes: ['145', '2019/01/01']") -]) +@pytest.mark.parametrize( + ('parameter', 'expression', 'message'), + [ + ( + MacAddressListParamType(' '), + 'D4:6A:6A:12:B0:75 foo 00:00:00:00:00', + "mac addresses: ['foo', '00:00:00:00:00']", + ), + (UUIDListParamType(' '), 'foo a7309d0b-c858-4d54-b6e1-1c20f8c22047 142-48dr', "uuid: ['foo', '142-48dr']"), + (DateTimeListParamType(' '), '145 2019-01-01 2019/01/01', "datetimes: ['145', '2019/01/01']"), + ], +) def test_should_print_error_when_giving_incorrect_option_for_list_types(runner, parameter, expression, message): @click.command() @click.option('-v', 'values', type=parameter) @@ -58,14 +77,18 @@ def cli(values): assert_in_output(2, f'These items are not {message}', result) -@pytest.mark.parametrize(('parameter', 'expression', 'expected_output'), [ - (JSON, '2', '2\n'), - (JSON, '"1.5"', '1.5\n'), - (JSON, '{"b": 2, "a": "foo"}', "{'b': 2, 'a': 'foo'}\n"), - (MAC_ADDRESS, '01:23:45:67:ab:CD', '01:23:45:67:ab:CD\n') -]) -def test_should_print_correct_output_when_giving_correct_option_for_simple_types(runner, parameter, expression, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'expression', 'expected_output'), + [ + (JSON, '2', '2\n'), + (JSON, '"1.5"', '1.5\n'), + (JSON, '{"b": 2, "a": "foo"}', "{'b': 2, 'a': 'foo'}\n"), + (MAC_ADDRESS, '01:23:45:67:ab:CD', '01:23:45:67:ab:CD\n'), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_simple_types( + runner, parameter, expression, expected_output +): @click.command() @click.option('-j', 'json_string', type=parameter) def cli(json_string): @@ -76,31 +99,54 @@ def cli(json_string): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize(('parameter', 'expression', 'expected_output'), [ - # string list - (StringListParamType(), 'foo,bar', "['foo', 'bar']\n"), - (StringListParamType(), '', "['']\n"), - (StringListParamType(' '), '1 2 foo', "['1', '2', 'foo']\n"), - # choice list - (ChoiceListParamType(['a', 'b', 'c']), 'a,b', "['a', 'b']\n"), - (ChoiceListParamType(['a', 'b', 'c'], separator=' '), 'a b c', "['a', 'b', 'c']\n"), - # mac address list - (MacAddressListParamType(), 'D4:6A:6A:12:B0:75,01:23:45:67:ab:CD', "['D4:6A:6A:12:B0:75', '01:23:45:67:ab:CD']\n"), - (MacAddressListParamType(' '), 'D4:6A:6A:12:B0:75 01:23:45:67:ab:CD', - "['D4:6A:6A:12:B0:75', '01:23:45:67:ab:CD']\n"), - # uuid list - (UUIDListParamType(), 'a7309d0b-c858-4d54-b6e1-1c20f8c22047,bfa65f3c-e6ac-4844-8e09-e84535f8cdc5', - "[UUID('a7309d0b-c858-4d54-b6e1-1c20f8c22047'), UUID('bfa65f3c-e6ac-4844-8e09-e84535f8cdc5')]\n"), - (UUIDListParamType(' '), 'a7309d0b-c858-4d54-b6e1-1c20f8c22047 bfa65f3c-e6ac-4844-8e09-e84535f8cdc5', - "[UUID('a7309d0b-c858-4d54-b6e1-1c20f8c22047'), UUID('bfa65f3c-e6ac-4844-8e09-e84535f8cdc5')]\n"), - # datetime list - (DateTimeListParamType(), '2019-01-01,2019-01-01 01:00:00', - '[datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 1, 1, 0)]\n'), - (DateTimeListParamType(', '), '2019-01-01, 2019-01-01 01:00:00', - '[datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 1, 1, 0)]\n') -]) -def test_should_print_correct_output_when_giving_correct_option_for_list_types(runner, parameter, expression, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'expression', 'expected_output'), + [ + # string list + (StringListParamType(), 'foo,bar', "['foo', 'bar']\n"), + (StringListParamType(), '', "['']\n"), + (StringListParamType(' '), '1 2 foo', "['1', '2', 'foo']\n"), + # choice list + (ChoiceListParamType(['a', 'b', 'c']), 'a,b', "['a', 'b']\n"), + (ChoiceListParamType(['a', 'b', 'c'], separator=' '), 'a b c', "['a', 'b', 'c']\n"), + # mac address list + ( + MacAddressListParamType(), + 'D4:6A:6A:12:B0:75,01:23:45:67:ab:CD', + "['D4:6A:6A:12:B0:75', '01:23:45:67:ab:CD']\n", + ), + ( + MacAddressListParamType(' '), + 'D4:6A:6A:12:B0:75 01:23:45:67:ab:CD', + "['D4:6A:6A:12:B0:75', '01:23:45:67:ab:CD']\n", + ), + # uuid list + ( + UUIDListParamType(), + 'a7309d0b-c858-4d54-b6e1-1c20f8c22047,bfa65f3c-e6ac-4844-8e09-e84535f8cdc5', + "[UUID('a7309d0b-c858-4d54-b6e1-1c20f8c22047'), UUID('bfa65f3c-e6ac-4844-8e09-e84535f8cdc5')]\n", + ), + ( + UUIDListParamType(' '), + 'a7309d0b-c858-4d54-b6e1-1c20f8c22047 bfa65f3c-e6ac-4844-8e09-e84535f8cdc5', + "[UUID('a7309d0b-c858-4d54-b6e1-1c20f8c22047'), UUID('bfa65f3c-e6ac-4844-8e09-e84535f8cdc5')]\n", + ), + # datetime list + ( + DateTimeListParamType(), + '2019-01-01,2019-01-01 01:00:00', + '[datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 1, 1, 0)]\n', + ), + ( + DateTimeListParamType(', '), + '2019-01-01, 2019-01-01 01:00:00', + '[datetime.datetime(2019, 1, 1, 0, 0), datetime.datetime(2019, 1, 1, 1, 0)]\n', + ), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_list_types( + runner, parameter, expression, expected_output +): @click.command() @click.option('-v', 'values', type=parameter) def cli(values): @@ -111,9 +157,9 @@ def cli(values): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize("param_type", [ - StringListParamType, MacAddressListParamType, UUIDListParamType, DateTimeListParamType -]) +@pytest.mark.parametrize( + "param_type", [StringListParamType, MacAddressListParamType, UUIDListParamType, DateTimeListParamType] +) def test_miscellaneous_list_param_types_ignore_empty_string(param_type): misc_list_type = param_type(ignore_empty=True) @@ -128,8 +174,15 @@ def test_should_call_json_loads_with_correct_arguments(self, mocker): json_type = JsonParamType(parse_float=Decimal, parse_int=int, parse_constant=Decimal) json_type.convert(2, None, None) - loads_mock.assert_called_once_with(2, cls=None, object_hook=None, parse_float=Decimal, parse_int=int, - parse_constant=Decimal, object_pairs_hook=None) + loads_mock.assert_called_once_with( + 2, + cls=None, + object_hook=None, + parse_float=Decimal, + parse_int=int, + parse_constant=Decimal, + object_pairs_hook=None, + ) class TestFirstOf: @@ -143,39 +196,48 @@ class CoreNumber(FirstOf): assert 'CORE NUMBER' == repr(FirstOf(click.INT, click.Choice(['all', 'half']), name='core number')) assert '(INTEGER | CHOICE)' == repr(FirstOf(click.INT, click.Choice(['all', 'half']))) - @pytest.mark.parametrize(('expression', 'param_types', 'value'), [ - ('12', (click.INT,), 12), - ('auto', (click.Choice(['auto', 'full']), click.INT), 'auto'), - ('full', (click.Choice(['auto', 'full']), click.INT), 'full'), - ('12', (click.Choice(['auto', 'full']), click.INT), 12), - ('auto', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 'auto'), - ('full', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 'full'), - ('12', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 12), - ('12.3', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 12.3) - ]) + @pytest.mark.parametrize( + ('expression', 'param_types', 'value'), + [ + ('12', (click.INT,), 12), + ('auto', (click.Choice(['auto', 'full']), click.INT), 'auto'), + ('full', (click.Choice(['auto', 'full']), click.INT), 'full'), + ('12', (click.Choice(['auto', 'full']), click.INT), 12), + ('auto', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 'auto'), + ('full', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 'full'), + ('12', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 12), + ('12.3', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), 12.3), + ], + ) def test_should_parse_expression_successfully(self, expression, param_types, value): union_type = FirstOf(*param_types) converted_value = union_type.convert(expression, None, None) assert type(value) == type(converted_value) assert value == converted_value - - @pytest.mark.parametrize(('expression', 'param_types', 'expected_param_type'), [ - ('12', (click.INT,), click.INT), - ('auto', (click.Choice(['auto', 'full']), click.INT), click.Choice(['auto', 'full'])), - ('full', (click.Choice(['auto', 'full']), click.INT), click.Choice(['auto', 'full'])), - ('12', (click.Choice(['auto', 'full']), click.INT), click.INT), - ('12.3', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), click.FLOAT) - ]) + + @pytest.mark.parametrize( + ('expression', 'param_types', 'expected_param_type'), + [ + ('12', (click.INT,), click.INT), + ('auto', (click.Choice(['auto', 'full']), click.INT), click.Choice(['auto', 'full'])), + ('full', (click.Choice(['auto', 'full']), click.INT), click.Choice(['auto', 'full'])), + ('12', (click.Choice(['auto', 'full']), click.INT), click.INT), + ('12.3', (click.Choice(['auto', 'full']), click.INT, click.FLOAT), click.FLOAT), + ], + ) def test_should_return_correct_param_type(self, expression, param_types, expected_param_type): union_type = FirstOf(*param_types, return_param=True) (param_type, _) = union_type.convert(expression, None, None) assert repr(expected_param_type) == repr(param_type) - @pytest.mark.parametrize(('expression', 'param_types'), [ - ('auto', (click.INT,)), - ('12.6', (click.Choice(['auto', 'full']), click.INT)), - ('bla', (click.Choice(['auto', 'full']), click.INT, click.FLOAT)), - ]) + @pytest.mark.parametrize( + ('expression', 'param_types'), + [ + ('auto', (click.INT,)), + ('12.6', (click.Choice(['auto', 'full']), click.INT)), + ('bla', (click.Choice(['auto', 'full']), click.INT, click.FLOAT)), + ], + ) def test_should_parse_expression_unsuccessfully(self, expression, param_types): union_type = FirstOf(*param_types) with pytest.raises(click.BadParameter, match=r'.*\n - '.join(p.name.upper() for p in param_types)): diff --git a/tests/test_network.py b/tests/test_network.py index 14b4af5..1c0f540 100644 --- a/tests/test_network.py +++ b/tests/test_network.py @@ -4,73 +4,99 @@ import pytest from click_params.network import ( - IP_ADDRESS, IPV4_ADDRESS, IPV6_ADDRESS, IP_NETWORK, IPV4_NETWORK, IPV6_NETWORK, Ipv4AddressRange, Ipv6AddressRange, - IpAddressListParamType, Ipv4AddressListParamType, Ipv6AddressListParamType, IpNetworkListParamType, - Ipv4NetworkListParamType, Ipv6NetworkListParamType + IP_ADDRESS, + IP_NETWORK, + IPV4_ADDRESS, + IPV4_NETWORK, + IPV6_ADDRESS, + IPV6_NETWORK, + IpAddressListParamType, + IpNetworkListParamType, + Ipv4AddressListParamType, + Ipv4AddressRange, + Ipv4NetworkListParamType, + Ipv6AddressListParamType, + Ipv6AddressRange, + Ipv6NetworkListParamType, +) +from tests.helpers import assert_equals_output, assert_in_output + + +@pytest.mark.parametrize( + ('name', 'parameter'), + [ + ('ip address', IP_ADDRESS), + ('ipv4 address', IPV4_ADDRESS), + ('ipv6 address', IPV6_ADDRESS), + ('ip network', IP_NETWORK), + ('ip address list', IpAddressListParamType()), + ('ipv4 address list', Ipv4AddressListParamType()), + ('ipv6 address list', Ipv6AddressListParamType()), + ('ip network list', IpNetworkListParamType()), + ('ipv4 network list', Ipv4NetworkListParamType()), + ('ipv6 network list', Ipv6NetworkListParamType()), + ], ) -from tests.helpers import assert_in_output, assert_equals_output - - -@pytest.mark.parametrize(('name', 'parameter'), [ - ('ip address', IP_ADDRESS), - ('ipv4 address', IPV4_ADDRESS), - ('ipv6 address', IPV6_ADDRESS), - ('ip network', IP_NETWORK), - ('ip address list', IpAddressListParamType()), - ('ipv4 address list', Ipv4AddressListParamType()), - ('ipv6 address list', Ipv6AddressListParamType()), - ('ip network list', IpNetworkListParamType()), - ('ipv4 network list', Ipv4NetworkListParamType()), - ('ipv6 network list', Ipv6NetworkListParamType()) -]) def test_parameter_name_and_representation_are_correct_for_simple_and_list_types(name, parameter): assert name == parameter.name assert name.upper() == repr(parameter) -@pytest.mark.parametrize(('name', 'representation', 'parameter'), [ - ('ipv4 address range', f'IPV4AddressRange({repr(IPv4Address("127.0.0.1"))}, {repr(IPv4Address("127.0.0.5"))})', - Ipv4AddressRange(IPv4Address('127.0.0.1'), IPv4Address('127.0.0.5'))), - ('ipv6 address range', f'IPV6AddressRange({repr(IPv6Address("::1"))}, {repr(IPv6Address("::10"))})', - Ipv6AddressRange(IPv6Address('::1'), IPv6Address('::10'))) -]) +@pytest.mark.parametrize( + ('name', 'representation', 'parameter'), + [ + ( + 'ipv4 address range', + f'IPV4AddressRange({repr(IPv4Address("127.0.0.1"))}, {repr(IPv4Address("127.0.0.5"))})', + Ipv4AddressRange(IPv4Address('127.0.0.1'), IPv4Address('127.0.0.5')), + ), + ( + 'ipv6 address range', + f'IPV6AddressRange({repr(IPv6Address("::1"))}, {repr(IPv6Address("::10"))})', + Ipv6AddressRange(IPv6Address('::1'), IPv6Address('::10')), + ), + ], +) def test_parameter_name_and_representation_are_correct_for_range_types(name, representation, parameter): assert name == parameter.name assert representation == repr(parameter) -@pytest.mark.parametrize(('parameter', 'param_value'), [ - # generic ip address (ipv4 or ipv6) - (IP_ADDRESS, 'foo'), - (IP_ADDRESS, '1245'), - (IP_ADDRESS, '125.5'), - # ipv4 address - (IPV4_ADDRESS, 'foo'), - (IPV4_ADDRESS, '1245'), - (IPV4_ADDRESS, '125.5'), - # ipv6 address - (IPV6_ADDRESS, 'foo'), - (IPV6_ADDRESS, '1245'), - (IPV6_ADDRESS, '125.5'), - # generic ip network (ipv4 or ipv6) - (IP_NETWORK, 'foo'), - (IP_NETWORK, '1245'), - (IP_NETWORK, '1452.5'), - (IP_NETWORK, '12.0.0.0/45'), - (IP_NETWORK, '1245/24'), - (IP_NETWORK, '2001:db00::0/ffff:ff00::'), - # ipv4 network - (IPV4_NETWORK, 'foo'), - (IPV4_NETWORK, '1245'), - (IPV4_NETWORK, '1452.5'), - (IPV4_NETWORK, '12.0.0.0/45'), - (IPV4_NETWORK, '1245/24'), - # ipv6 network - (IPV6_NETWORK, 'foo'), - (IPV6_NETWORK, '1245'), - (IPV6_NETWORK, '1452.5'), - (IPV6_NETWORK, '2001:db00::0/ffff:ff00::'), -]) +@pytest.mark.parametrize( + ('parameter', 'param_value'), + [ + # generic ip address (ipv4 or ipv6) + (IP_ADDRESS, 'foo'), + (IP_ADDRESS, '1245'), + (IP_ADDRESS, '125.5'), + # ipv4 address + (IPV4_ADDRESS, 'foo'), + (IPV4_ADDRESS, '1245'), + (IPV4_ADDRESS, '125.5'), + # ipv6 address + (IPV6_ADDRESS, 'foo'), + (IPV6_ADDRESS, '1245'), + (IPV6_ADDRESS, '125.5'), + # generic ip network (ipv4 or ipv6) + (IP_NETWORK, 'foo'), + (IP_NETWORK, '1245'), + (IP_NETWORK, '1452.5'), + (IP_NETWORK, '12.0.0.0/45'), + (IP_NETWORK, '1245/24'), + (IP_NETWORK, '2001:db00::0/ffff:ff00::'), + # ipv4 network + (IPV4_NETWORK, 'foo'), + (IPV4_NETWORK, '1245'), + (IPV4_NETWORK, '1452.5'), + (IPV4_NETWORK, '12.0.0.0/45'), + (IPV4_NETWORK, '1245/24'), + # ipv6 network + (IPV6_NETWORK, 'foo'), + (IPV6_NETWORK, '1245'), + (IPV6_NETWORK, '1452.5'), + (IPV6_NETWORK, '2001:db00::0/ffff:ff00::'), + ], +) def test_should_print_error_when_giving_incorrect_option_for_simple_types(runner, parameter, param_value): @click.command() @click.option('-i', '--ip', type=parameter) @@ -82,15 +108,21 @@ def cli(ip): assert_in_output(2, f'{param_value} is not a valid {parameter.name}', result) -@pytest.mark.parametrize(('parameter', 'expression', 'message'), [ - (IpAddressListParamType(' '), 'foo 10.0.0.1 1452', "ip addresses: ['foo', '1452']"), - (Ipv4AddressListParamType(', '), '10.0.0.1, foo, ::1', "ipv4 addresses: ['foo', '::1']"), - (Ipv6AddressListParamType(' '), '::1 foo ::dead:beef 10.0.0.1', "ipv6 addresses: ['foo', '10.0.0.1']"), - (IpNetworkListParamType(' '), '192.168.1.0/24 foo 1254 2001:db00::/24', "ip networks: ['foo', '1254']"), - (Ipv4NetworkListParamType(' '), '10.0.0.0/8 152 192.168.1.0/24', "ipv4 networks: ['152']"), - (Ipv6NetworkListParamType(' '), '2001:db00::/24 foo 2001:db00::0/ffff:ff00::', - "ipv6 networks: ['foo', '2001:db00::0/ffff:ff00::']") -]) +@pytest.mark.parametrize( + ('parameter', 'expression', 'message'), + [ + (IpAddressListParamType(' '), 'foo 10.0.0.1 1452', "ip addresses: ['foo', '1452']"), + (Ipv4AddressListParamType(', '), '10.0.0.1, foo, ::1', "ipv4 addresses: ['foo', '::1']"), + (Ipv6AddressListParamType(' '), '::1 foo ::dead:beef 10.0.0.1', "ipv6 addresses: ['foo', '10.0.0.1']"), + (IpNetworkListParamType(' '), '192.168.1.0/24 foo 1254 2001:db00::/24', "ip networks: ['foo', '1254']"), + (Ipv4NetworkListParamType(' '), '10.0.0.0/8 152 192.168.1.0/24', "ipv4 networks: ['152']"), + ( + Ipv6NetworkListParamType(' '), + '2001:db00::/24 foo 2001:db00::0/ffff:ff00::', + "ipv6 networks: ['foo', '2001:db00::0/ffff:ff00::']", + ), + ], +) def test_should_print_error_when_giving_incorrect_option_for_list_types(runner, parameter, expression, message): @click.command() @click.option('-v', 'values', type=parameter) @@ -102,13 +134,21 @@ def cli(values): assert_in_output(2, f'These items are not {message}', result) -@pytest.mark.parametrize(('parameter', 'value', 'message'), [ - (Ipv4AddressRange(IPv4Address('192.168.1.1'), IPv4Address('192.168.1.254')), '192.169.1.1', - '192.169.1.1 is not in the valid range of 192.168.1.1 to 192.168.1.254.'), - (Ipv6AddressRange(IPv6Address('2001:db00::1'), IPv6Address('2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe')), - IPv6Address('2001:dc00::9'), - '2001:dc00::9 is not in the valid range of 2001:db00::1 to 2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe.') -]) +@pytest.mark.parametrize( + ('parameter', 'value', 'message'), + [ + ( + Ipv4AddressRange(IPv4Address('192.168.1.1'), IPv4Address('192.168.1.254')), + '192.169.1.1', + '192.169.1.1 is not in the valid range of 192.168.1.1 to 192.168.1.254.', + ), + ( + Ipv6AddressRange(IPv6Address('2001:db00::1'), IPv6Address('2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe')), + IPv6Address('2001:dc00::9'), + '2001:dc00::9 is not in the valid range of 2001:db00::1 to 2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe.', + ), + ], +) def test_should_print_error_when_giving_value_is_out_of_limits(runner, parameter, value, message): @click.command() @click.option('-c', 'count', type=parameter) @@ -120,21 +160,27 @@ def cli(count): assert_in_output(2, message, result) -@pytest.mark.parametrize(('parameter', 'param_value'), [ - (IP_ADDRESS, '192.168.1.1'), - (IP_ADDRESS, '::dead:beef'), - (IPV4_ADDRESS, '192.168.1.1'), - (IPV6_ADDRESS, '::dead:beef'), - (IP_NETWORK, '192.168.0.0/24'), - (IP_NETWORK, '2001:db00::/24'), - (IPV4_NETWORK, '192.168.0.0/24'), - (IPV6_NETWORK, '2001:db00::/24'), - (Ipv4AddressRange(IPv4Address('192.168.1.1'), IPv4Address('192.168.1.254')), '192.168.1.1'), - (Ipv6AddressRange(IPv6Address('2001:db00::1'), IPv6Address('2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe')), - '2001:db00::4') -]) -def test_should_print_correct_output_when_giving_correct_option_for_simple_and_range_types(runner, parameter, - param_value): +@pytest.mark.parametrize( + ('parameter', 'param_value'), + [ + (IP_ADDRESS, '192.168.1.1'), + (IP_ADDRESS, '::dead:beef'), + (IPV4_ADDRESS, '192.168.1.1'), + (IPV6_ADDRESS, '::dead:beef'), + (IP_NETWORK, '192.168.0.0/24'), + (IP_NETWORK, '2001:db00::/24'), + (IPV4_NETWORK, '192.168.0.0/24'), + (IPV6_NETWORK, '2001:db00::/24'), + (Ipv4AddressRange(IPv4Address('192.168.1.1'), IPv4Address('192.168.1.254')), '192.168.1.1'), + ( + Ipv6AddressRange(IPv6Address('2001:db00::1'), IPv6Address('2001:dbff:ffff:ffff:ffff:ffff:ffff:fffe')), + '2001:db00::4', + ), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_simple_and_range_types( + runner, parameter, param_value +): @click.command() @click.option('-i', '--ip', type=parameter) def cli(ip): @@ -145,35 +191,68 @@ def cli(ip): assert_equals_output(0, f'{param_value}\n', result) -@pytest.mark.parametrize(('parameter', 'expression', 'expected_output'), [ - # ip address list - (IpAddressListParamType(), '192.168.1.2,::dead:beef', "[IPv4Address('192.168.1.2'), IPv6Address('::dead:beef')]\n"), - (IpAddressListParamType(' '), '192.168.1.2 ::dead:beef', - "[IPv4Address('192.168.1.2'), IPv6Address('::dead:beef')]\n"), - # ipv4 address list - (Ipv4AddressListParamType(), '10.0.0.1,192.168.1.2', "[IPv4Address('10.0.0.1'), IPv4Address('192.168.1.2')]\n"), - (Ipv4AddressListParamType(' '), '10.0.0.1 192.168.1.2', "[IPv4Address('10.0.0.1'), IPv4Address('192.168.1.2')]\n"), - # ipv6 address list - (Ipv6AddressListParamType(), '::1,::dead:beef', "[IPv6Address('::1'), IPv6Address('::dead:beef')]\n"), - (Ipv6AddressListParamType(', '), '::1, ::dead:beef', "[IPv6Address('::1'), IPv6Address('::dead:beef')]\n"), - # ip network list - (IpNetworkListParamType(), '192.168.1.0/24,2001:db00::/24', - "[IPv4Network('192.168.1.0/24'), IPv6Network('2001:db00::/24')]\n"), - (IpNetworkListParamType(' '), '192.168.1.0/24 2001:db00::/24', - "[IPv4Network('192.168.1.0/24'), IPv6Network('2001:db00::/24')]\n"), - # ipv4 network list - (Ipv4NetworkListParamType(), '10.0.0.0/8,192.168.1.0/24', - "[IPv4Network('10.0.0.0/8'), IPv4Network('192.168.1.0/24')]\n"), - (Ipv4NetworkListParamType(', '), '10.0.0.0/8, 192.168.1.0/24', - "[IPv4Network('10.0.0.0/8'), IPv4Network('192.168.1.0/24')]\n"), - # ipv6 network list - (Ipv6NetworkListParamType(), '2001:db00::/24,2001:db8:1234::/48', - "[IPv6Network('2001:db00::/24'), IPv6Network('2001:db8:1234::/48')]\n"), - (Ipv6NetworkListParamType(', '), '2001:db00::/24, 2001:db8:1234::/48', - "[IPv6Network('2001:db00::/24'), IPv6Network('2001:db8:1234::/48')]\n"), -]) -def test_should_print_correct_output_when_giving_correct_option_for_list_types(runner, parameter, expression, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'expression', 'expected_output'), + [ + # ip address list + ( + IpAddressListParamType(), + '192.168.1.2,::dead:beef', + "[IPv4Address('192.168.1.2'), IPv6Address('::dead:beef')]\n", + ), + ( + IpAddressListParamType(' '), + '192.168.1.2 ::dead:beef', + "[IPv4Address('192.168.1.2'), IPv6Address('::dead:beef')]\n", + ), + # ipv4 address list + (Ipv4AddressListParamType(), '10.0.0.1,192.168.1.2', "[IPv4Address('10.0.0.1'), IPv4Address('192.168.1.2')]\n"), + ( + Ipv4AddressListParamType(' '), + '10.0.0.1 192.168.1.2', + "[IPv4Address('10.0.0.1'), IPv4Address('192.168.1.2')]\n", + ), + # ipv6 address list + (Ipv6AddressListParamType(), '::1,::dead:beef', "[IPv6Address('::1'), IPv6Address('::dead:beef')]\n"), + (Ipv6AddressListParamType(', '), '::1, ::dead:beef', "[IPv6Address('::1'), IPv6Address('::dead:beef')]\n"), + # ip network list + ( + IpNetworkListParamType(), + '192.168.1.0/24,2001:db00::/24', + "[IPv4Network('192.168.1.0/24'), IPv6Network('2001:db00::/24')]\n", + ), + ( + IpNetworkListParamType(' '), + '192.168.1.0/24 2001:db00::/24', + "[IPv4Network('192.168.1.0/24'), IPv6Network('2001:db00::/24')]\n", + ), + # ipv4 network list + ( + Ipv4NetworkListParamType(), + '10.0.0.0/8,192.168.1.0/24', + "[IPv4Network('10.0.0.0/8'), IPv4Network('192.168.1.0/24')]\n", + ), + ( + Ipv4NetworkListParamType(', '), + '10.0.0.0/8, 192.168.1.0/24', + "[IPv4Network('10.0.0.0/8'), IPv4Network('192.168.1.0/24')]\n", + ), + # ipv6 network list + ( + Ipv6NetworkListParamType(), + '2001:db00::/24,2001:db8:1234::/48', + "[IPv6Network('2001:db00::/24'), IPv6Network('2001:db8:1234::/48')]\n", + ), + ( + Ipv6NetworkListParamType(', '), + '2001:db00::/24, 2001:db8:1234::/48', + "[IPv6Network('2001:db00::/24'), IPv6Network('2001:db8:1234::/48')]\n", + ), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_list_types( + runner, parameter, expression, expected_output +): @click.command() @click.option('-v', 'values', type=parameter) def cli(values): @@ -184,10 +263,17 @@ def cli(values): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize("param_type", [ - IpAddressListParamType, Ipv4AddressListParamType, Ipv6AddressListParamType, IpNetworkListParamType, - Ipv4NetworkListParamType, Ipv6NetworkListParamType -]) +@pytest.mark.parametrize( + "param_type", + [ + IpAddressListParamType, + Ipv4AddressListParamType, + Ipv6AddressListParamType, + IpNetworkListParamType, + Ipv4NetworkListParamType, + Ipv6NetworkListParamType, + ], +) def test_network_list_param_types_ignore_empty_string(param_type): network_list_type = param_type(ignore_empty=True) diff --git a/tests/test_numeric.py b/tests/test_numeric.py index 2b89fd2..f28c493 100644 --- a/tests/test_numeric.py +++ b/tests/test_numeric.py @@ -5,44 +5,67 @@ import pytest from click_params.numeric import ( - DECIMAL, FRACTION, COMPLEX, DecimalRange, FractionRange, IntListParamType, FloatListParamType, DecimalListParamType, - FractionListParamType, ComplexListParamType + COMPLEX, + DECIMAL, + FRACTION, + ComplexListParamType, + DecimalListParamType, + DecimalRange, + FloatListParamType, + FractionListParamType, + FractionRange, + IntListParamType, +) +from tests.helpers import assert_equals_output, assert_in_output + + +@pytest.mark.parametrize( + ('name', 'parameter'), + [ + ('decimal', DECIMAL), + ('fraction', FRACTION), + ('complex', COMPLEX), + ('int list', IntListParamType()), + ('float list', FloatListParamType()), + ('decimal list', DecimalListParamType()), + ('fraction list', FractionListParamType()), + ('complex list', ComplexListParamType()), + ], ) -from tests.helpers import assert_in_output, assert_equals_output - - -@pytest.mark.parametrize(('name', 'parameter'), [ - ('decimal', DECIMAL), - ('fraction', FRACTION), - ('complex', COMPLEX), - ('int list', IntListParamType()), - ('float list', FloatListParamType()), - ('decimal list', DecimalListParamType()), - ('fraction list', FractionListParamType()), - ('complex list', ComplexListParamType()), -]) def test_parameter_name_and_representation_are_correct_for_simple_and_list_types(name, parameter): assert name == parameter.name assert name.upper() == repr(parameter) -@pytest.mark.parametrize(('name', 'representation', 'parameter'), [ - ('decimal range', f'DecimalRange({repr(Decimal(0.1))}, {repr(Decimal(0.8))})', - DecimalRange(Decimal(0.1), Decimal(0.8))), - ('fraction range', f'FractionRange({repr(Fraction(0.1))}, {repr(Fraction(0.8))})', - FractionRange(Fraction(0.1), Fraction(0.8))) -]) +@pytest.mark.parametrize( + ('name', 'representation', 'parameter'), + [ + ( + 'decimal range', + f'DecimalRange({repr(Decimal(0.1))}, {repr(Decimal(0.8))})', + DecimalRange(Decimal(0.1), Decimal(0.8)), + ), + ( + 'fraction range', + f'FractionRange({repr(Fraction(0.1))}, {repr(Fraction(0.8))})', + FractionRange(Fraction(0.1), Fraction(0.8)), + ), + ], +) def test_parameter_name_and_representation_are_correct_for_range_types(name, representation, parameter): assert name == parameter.name assert representation == repr(parameter) -@pytest.mark.parametrize(('parameter', 'str_type', 'param_value'), [ - (DECIMAL, 'decimal', 'foo'), - (FRACTION, 'fraction', 'foo'), - (FRACTION, 'fraction', '2/0'), - (COMPLEX, 'complex', 'foo'), -]) +@pytest.mark.parametrize( + ('parameter', 'str_type', 'param_value'), + [ + (DECIMAL, 'decimal', 'foo'), + (FRACTION, 'fraction', 'foo'), + (FRACTION, 'fraction', '2/0'), + (COMPLEX, 'complex', 'foo'), + ], +) def test_should_print_error_when_giving_incorrect_option_for_simple_types(runner, parameter, str_type, param_value): @click.command() @click.option('-v', 'value', type=parameter) @@ -54,13 +77,16 @@ def cli(value): assert_in_output(2, f'{param_value} is not a valid {str_type}', result) -@pytest.mark.parametrize(('parameter', 'expression', 'message'), [ - (IntListParamType(), '1,foo,2,2.5', "integers: ['foo', '2.5']"), - (FloatListParamType(), '1.2,foo,2.5,bar', "floating point values: ['foo', 'bar']"), - (DecimalListParamType(), '1.2,foo,2.5,bar', "decimal values: ['foo', 'bar']"), - (FractionListParamType(' '), '1/3 foo/2 2.5 3/bar tar', "fractions: ['foo/2', '3/bar', 'tar']"), - (ComplexListParamType(' '), '5 foo 2+1j 1.4 bar', "complex values: ['foo', 'bar']") -]) +@pytest.mark.parametrize( + ('parameter', 'expression', 'message'), + [ + (IntListParamType(), '1,foo,2,2.5', "integers: ['foo', '2.5']"), + (FloatListParamType(), '1.2,foo,2.5,bar', "floating point values: ['foo', 'bar']"), + (DecimalListParamType(), '1.2,foo,2.5,bar', "decimal values: ['foo', 'bar']"), + (FractionListParamType(' '), '1/3 foo/2 2.5 3/bar tar', "fractions: ['foo/2', '3/bar', 'tar']"), + (ComplexListParamType(' '), '5 foo 2+1j 1.4 bar', "complex values: ['foo', 'bar']"), + ], +) def test_should_print_error_when_giving_incorrect_option_for_list_types(runner, parameter, expression, message): @click.command() @click.option('-v', 'values', type=parameter) @@ -72,10 +98,13 @@ def cli(values): assert_in_output(2, f'These items are not {message}', result) -@pytest.mark.parametrize(('parameter', 'value', 'message'), [ - (DecimalRange(Decimal('0.1'), Decimal('0.8')), '0.01', '0.01 is not in the valid range of 0.1 to 0.8.'), - (FractionRange(Fraction('0.1'), Fraction('0.8')), '0.01', '1/100 is not in the valid range of 1/10 to 4/5.') -]) +@pytest.mark.parametrize( + ('parameter', 'value', 'message'), + [ + (DecimalRange(Decimal('0.1'), Decimal('0.8')), '0.01', '0.01 is not in the valid range of 0.1 to 0.8.'), + (FractionRange(Fraction('0.1'), Fraction('0.8')), '0.01', '1/100 is not in the valid range of 1/10 to 4/5.'), + ], +) def test_should_print_error_when_giving_value_is_out_of_limits(runner, parameter, value, message): @click.command() @click.option('-c', 'count', type=parameter) @@ -87,18 +116,21 @@ def cli(count): assert_in_output(2, message, result) -@pytest.mark.parametrize(('parameter', 'param_value', 'expected_output'), [ - (DECIMAL, '5.0', '5.0\n'), - (FRACTION, '5.0', '5\n'), - (FRACTION, '1/3', '1/3\n'), - (COMPLEX, '5', '(5+0j)\n'), - (COMPLEX, '1+2j', '(1+2j)\n'), - (DecimalRange(Decimal('0.1'), Decimal('0.8')), '0.4', '0.4\n'), - (FractionRange(Fraction('0.1'), Fraction('0.8')), '0.4', '2/5\n') -]) -def test_should_print_correct_output_when_giving_correct_option_for_simple_and_range_types(runner, parameter, - param_value, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'param_value', 'expected_output'), + [ + (DECIMAL, '5.0', '5.0\n'), + (FRACTION, '5.0', '5\n'), + (FRACTION, '1/3', '1/3\n'), + (COMPLEX, '5', '(5+0j)\n'), + (COMPLEX, '1+2j', '(1+2j)\n'), + (DecimalRange(Decimal('0.1'), Decimal('0.8')), '0.4', '0.4\n'), + (FractionRange(Fraction('0.1'), Fraction('0.8')), '0.4', '2/5\n'), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_simple_and_range_types( + runner, parameter, param_value, expected_output +): @click.command() @click.option('-v', 'value', type=parameter) def cli(value): @@ -109,25 +141,29 @@ def cli(value): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize(('parameter', 'expression', 'expected_output'), [ - # int list - (IntListParamType(), '1,2', '[1, 2]\n'), - (IntListParamType(';'), '1;2', '[1, 2]\n'), - # float list - (FloatListParamType(), '1,.2', '[1.0, 0.2]\n'), - (FloatListParamType('; '), '1; .2', '[1.0, 0.2]\n'), - # decimal list - (DecimalListParamType(), '1,.2', "[Decimal('1'), Decimal('0.2')]\n"), - (DecimalListParamType(' '), '1 .2', "[Decimal('1'), Decimal('0.2')]\n"), - # fraction list - (FractionListParamType(), '1/3,.5', '[Fraction(1, 3), Fraction(1, 2)]\n'), - (FractionListParamType(' '), '1/3 .5', '[Fraction(1, 3), Fraction(1, 2)]\n'), - # complex list - (ComplexListParamType(), '5,1.4,2+1j', '[(5+0j), (1.4+0j), (2+1j)]\n'), - (ComplexListParamType(', '), '5, 1.4, 2+1j', '[(5+0j), (1.4+0j), (2+1j)]\n'), -]) -def test_should_print_correct_output_when_giving_correct_option_for_list_types(runner, parameter, expression, - expected_output): +@pytest.mark.parametrize( + ('parameter', 'expression', 'expected_output'), + [ + # int list + (IntListParamType(), '1,2', '[1, 2]\n'), + (IntListParamType(';'), '1;2', '[1, 2]\n'), + # float list + (FloatListParamType(), '1,.2', '[1.0, 0.2]\n'), + (FloatListParamType('; '), '1; .2', '[1.0, 0.2]\n'), + # decimal list + (DecimalListParamType(), '1,.2', "[Decimal('1'), Decimal('0.2')]\n"), + (DecimalListParamType(' '), '1 .2', "[Decimal('1'), Decimal('0.2')]\n"), + # fraction list + (FractionListParamType(), '1/3,.5', '[Fraction(1, 3), Fraction(1, 2)]\n'), + (FractionListParamType(' '), '1/3 .5', '[Fraction(1, 3), Fraction(1, 2)]\n'), + # complex list + (ComplexListParamType(), '5,1.4,2+1j', '[(5+0j), (1.4+0j), (2+1j)]\n'), + (ComplexListParamType(', '), '5, 1.4, 2+1j', '[(5+0j), (1.4+0j), (2+1j)]\n'), + ], +) +def test_should_print_correct_output_when_giving_correct_option_for_list_types( + runner, parameter, expression, expected_output +): @click.command() @click.option('-v', 'values', type=parameter) def cli(values): @@ -138,9 +174,10 @@ def cli(values): assert_equals_output(0, expected_output, result) -@pytest.mark.parametrize("param_type", [ - IntListParamType, FloatListParamType, ComplexListParamType, DecimalListParamType, FractionListParamType -]) +@pytest.mark.parametrize( + "param_type", + [IntListParamType, FloatListParamType, ComplexListParamType, DecimalListParamType, FractionListParamType], +) def test_numeric_list_param_types_ignore_empty_string(param_type): numeric_list_type = param_type(ignore_empty=True) diff --git a/tests/test_test_utils.py b/tests/test_test_utils.py index 4027946..ab05b0b 100644 --- a/tests/test_test_utils.py +++ b/tests/test_test_utils.py @@ -1,7 +1,7 @@ import pytest -from click.testing import Result, CliRunner +from click.testing import CliRunner, Result -from click_params.test_utils import assert_in_output, assert_equals_output, assert_list_in_output +from click_params.test_utils import assert_equals_output, assert_in_output, assert_list_in_output @pytest.mark.parametrize('callback', [assert_in_output, assert_equals_output, assert_list_in_output]) @@ -11,23 +11,25 @@ def test_should_raise_error_when_exit_code_is_different_from_the_result_one(call callback(0, '', result) -@pytest.mark.parametrize(('callback', 'data'), [ - (assert_in_output, 'bar'), - (assert_equals_output, 'bar'), - (assert_list_in_output, ['bar']), - (assert_list_in_output, ['f', 'a']) -]) +@pytest.mark.parametrize( + ('callback', 'data'), + [ + (assert_in_output, 'bar'), + (assert_equals_output, 'bar'), + (assert_list_in_output, ['bar']), + (assert_list_in_output, ['f', 'a']), + ], +) def test_should_raise_error_when_expected_output_not_in_result_output(callback, data): result = Result(CliRunner(), b'foo', b'', '', 0, None) with pytest.raises(AssertionError): callback(0, data, result) -@pytest.mark.parametrize(('callback', 'data'), [ - (assert_in_output, 'bar'), - (assert_equals_output, 'foobar'), - (assert_list_in_output, ['fo', 'ba']) -]) +@pytest.mark.parametrize( + ('callback', 'data'), + [(assert_in_output, 'bar'), (assert_equals_output, 'foobar'), (assert_list_in_output, ['fo', 'ba'])], +) def test_should_not_raise_error_when_exit_code_and_output_are_corresponding_to_result_properties(callback, data): result = Result(CliRunner(), b'foobar', b'', '', 0, None) try: