diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py index 3ee5dbaf0..e5f9a66ed 100644 --- a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/__init__.py @@ -4,6 +4,7 @@ from my_test_api_client.api.default import DefaultEndpoints from my_test_api_client.api.parameters import ParametersEndpoints +from my_test_api_client.api.tag1 import Tag1Endpoints from my_test_api_client.api.tests import TestsEndpoints @@ -19,3 +20,7 @@ def default(cls) -> Type[DefaultEndpoints]: @classmethod def parameters(cls) -> Type[ParametersEndpoints]: return ParametersEndpoints + + @classmethod + def tag1(cls) -> Type[Tag1Endpoints]: + return Tag1Endpoints diff --git a/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag1/__init__.py b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag1/__init__.py new file mode 100644 index 000000000..b91db35b0 --- /dev/null +++ b/end_to_end_tests/custom-templates-golden-record/my_test_api_client/api/tag1/__init__.py @@ -0,0 +1,11 @@ +""" Contains methods for accessing the API Endpoints """ + +import types + +from my_test_api_client.api.tag1 import get_tag_with_number + + +class Tag1Endpoints: + @classmethod + def get_tag_with_number(cls) -> types.ModuleType: + return get_tag_with_number diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/__init__.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py new file mode 100644 index 000000000..88e592ce3 --- /dev/null +++ b/end_to_end_tests/golden-record/my_test_api_client/api/tag1/get_tag_with_number.py @@ -0,0 +1,61 @@ +from typing import Any, Dict + +import httpx + +from ...client import Client +from ...types import Response + + +def _get_kwargs( + *, + client: Client, +) -> Dict[str, Any]: + url = "{}/tag_with_number".format(client.base_url) + + headers: Dict[str, Any] = client.get_headers() + cookies: Dict[str, Any] = client.get_cookies() + + return { + "url": url, + "headers": headers, + "cookies": cookies, + "timeout": client.get_timeout(), + } + + +def _build_response(*, response: httpx.Response) -> Response[Any]: + return Response( + status_code=response.status_code, + content=response.content, + headers=response.headers, + parsed=None, + ) + + +def sync_detailed( + *, + client: Client, +) -> Response[Any]: + kwargs = _get_kwargs( + client=client, + ) + + response = httpx.get( + **kwargs, + ) + + return _build_response(response=response) + + +async def asyncio_detailed( + *, + client: Client, +) -> Response[Any]: + kwargs = _get_kwargs( + client=client, + ) + + async with httpx.AsyncClient() as _client: + response = await _client.get(**kwargs) + + return _build_response(response=response) diff --git a/end_to_end_tests/openapi.json b/end_to_end_tests/openapi.json index 784942ba2..3724f428c 100644 --- a/end_to_end_tests/openapi.json +++ b/end_to_end_tests/openapi.json @@ -823,6 +823,16 @@ } } } + }, + "/tag_with_number": { + "get": { + "tags": [1], + "responses": { + "200": { + "description": "Success" + } + } + } } }, "components": { diff --git a/end_to_end_tests/test_end_to_end.py b/end_to_end_tests/test_end_to_end.py index bcc8b12e1..072dd48ad 100644 --- a/end_to_end_tests/test_end_to_end.py +++ b/end_to_end_tests/test_end_to_end.py @@ -1,3 +1,4 @@ +import os import shutil from filecmp import cmpfiles, dircmp from pathlib import Path @@ -101,18 +102,25 @@ def test_end_to_end(): def test_custom_templates(): expected_differences = {} # key: path relative to generated directory, value: expected generated content + api_dir = Path("my_test_api_client").joinpath("api") + golden_tpls_root_dir = Path(__file__).parent.joinpath("custom-templates-golden-record") + expected_difference_paths = [ Path("README.md"), - Path("my_test_api_client").joinpath("api", "__init__.py"), - Path("my_test_api_client").joinpath("api", "tests", "__init__.py"), - Path("my_test_api_client").joinpath("api", "default", "__init__.py"), - Path("my_test_api_client").joinpath("api", "parameters", "__init__.py"), + api_dir.joinpath("__init__.py"), ] - golden_tpls_root_dir = Path(__file__).parent.joinpath("custom-templates-golden-record") for expected_difference_path in expected_difference_paths: expected_differences[expected_difference_path] = (golden_tpls_root_dir / expected_difference_path).read_text() + # Each API module (defined by tag) has a custom __init__.py in it now. + for endpoint_mod in golden_tpls_root_dir.joinpath(api_dir).iterdir(): + if not endpoint_mod.is_dir(): + continue + relative_path = api_dir.joinpath(endpoint_mod.name, "__init__.py") + expected_text = endpoint_mod.joinpath("__init__.py").read_text() + expected_differences[relative_path] = expected_text + run_e2e_test( extra_args=["--custom-template-path=end_to_end_tests/test_custom_templates/"], expected_differences=expected_differences, diff --git a/openapi_python_client/config.py b/openapi_python_client/config.py index 11a1340b1..d93cef6ae 100644 --- a/openapi_python_client/config.py +++ b/openapi_python_client/config.py @@ -20,9 +20,6 @@ class Config(BaseModel): @staticmethod def load_from_path(path: Path) -> "Config": """Creates a Config from provided JSON or YAML file and sets a bunch of globals from it""" - from . import utils - config_data = yaml.safe_load(path.read_text()) config = Config(**config_data) - utils.FIELD_PREFIX = config.field_prefix return config diff --git a/openapi_python_client/parser/openapi.py b/openapi_python_client/parser/openapi.py index e92038ab6..e22b43a7c 100644 --- a/openapi_python_client/parser/openapi.py +++ b/openapi_python_client/parser/openapi.py @@ -30,9 +30,9 @@ class EndpointCollection: @staticmethod def from_data( *, data: Dict[str, oai.PathItem], schemas: Schemas, config: Config - ) -> Tuple[Dict[str, "EndpointCollection"], Schemas]: + ) -> Tuple[Dict[utils.PythonIdentifier, "EndpointCollection"], Schemas]: """Parse the openapi paths data to get EndpointCollections by tag""" - endpoints_by_tag: Dict[str, EndpointCollection] = {} + endpoints_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] = {} methods = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] @@ -41,7 +41,7 @@ def from_data( operation: Optional[oai.Operation] = getattr(path_data, method) if operation is None: continue - tag = utils.snake_case((operation.tags or ["default"])[0]) + tag = utils.PythonIdentifier(value=(operation.tags or ["default"])[0], prefix="tag") collection = endpoints_by_tag.setdefault(tag, EndpointCollection(tag=tag)) endpoint, schemas = Endpoint.from_data( data=operation, path=path, method=method, tag=tag, schemas=schemas, config=config @@ -261,8 +261,8 @@ def _add_parameters( if prop.python_name in used_python_names: duplicate, duplicate_location = used_python_names[prop.python_name] if duplicate.python_name == prop.python_name: # Existing should be converted too for consistency - duplicate.set_python_name(f"{duplicate.python_name}_{duplicate_location}") - prop.set_python_name(f"{prop.python_name}_{param.param_in}") + duplicate.set_python_name(f"{duplicate.python_name}_{duplicate_location}", config=config) + prop.set_python_name(f"{prop.python_name}_{param.param_in}", config=config) else: used_python_names[prop.python_name] = (prop, param.param_in) @@ -340,7 +340,7 @@ class GeneratorData: version: str models: Iterator[ModelProperty] errors: List[ParseError] - endpoint_collections_by_tag: Dict[str, EndpointCollection] + endpoint_collections_by_tag: Dict[utils.PythonIdentifier, EndpointCollection] enums: Iterator[EnumProperty] @staticmethod diff --git a/openapi_python_client/parser/properties/__init__.py b/openapi_python_client/parser/properties/__init__.py index e4800c6af..839ef05ff 100644 --- a/openapi_python_client/parser/properties/__init__.py +++ b/openapi_python_client/parser/properties/__init__.py @@ -177,7 +177,6 @@ class UnionProperty(Property): has_properties_without_templates: bool = attr.ib(init=False) def __attrs_post_init__(self) -> None: - super().__attrs_post_init__() object.__setattr__( self, "has_properties_without_templates", any(prop.template is None for prop in self.inner_properties) ) @@ -235,16 +234,18 @@ def inner_properties_with_template(self) -> Iterator[Property]: def _string_based_property( - name: str, required: bool, data: oai.Schema + name: str, required: bool, data: oai.Schema, config: Config ) -> Union[StringProperty, DateProperty, DateTimeProperty, FileProperty]: """Construct a Property from the type "string" """ string_format = data.schema_format + python_name = utils.PythonIdentifier(value=name, prefix=config.field_prefix) if string_format == "date-time": return DateTimeProperty( name=name, required=required, default=convert("datetime.datetime", data.default), nullable=data.nullable, + python_name=python_name, ) elif string_format == "date": return DateProperty( @@ -252,6 +253,7 @@ def _string_based_property( required=required, default=convert("datetime.date", data.default), nullable=data.nullable, + python_name=python_name, ) elif string_format == "binary": return FileProperty( @@ -259,6 +261,7 @@ def _string_based_property( required=required, default=None, nullable=data.nullable, + python_name=python_name, ) else: return StringProperty( @@ -267,6 +270,7 @@ def _string_based_property( required=required, pattern=data.pattern, nullable=data.nullable, + python_name=python_name, ) @@ -326,6 +330,7 @@ def build_enum_property( values=values, value_type=value_type, default=None, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ) default = get_enum_default(prop, data) @@ -373,6 +378,7 @@ def build_union_property( default=default, inner_properties=sub_properties, nullable=data.nullable, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ), schemas, ) @@ -395,6 +401,7 @@ def build_list_property( default=None, inner_property=inner_prop, nullable=data.nullable, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ), schemas, ) @@ -406,6 +413,7 @@ def _property_from_ref( parent: Union[oai.Schema, None], data: oai.Reference, schemas: Schemas, + config: Config, ) -> Tuple[Union[Property, PropertyError], Schemas]: ref_path = parse_reference_path(data.ref) if isinstance(ref_path, ParseError): @@ -414,7 +422,12 @@ def _property_from_ref( if not existing: return PropertyError(data=data, detail="Could not find reference in parsed models or enums"), schemas - prop = attr.evolve(existing, required=required, name=name) + prop = attr.evolve( + existing, + required=required, + name=name, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), + ) if parent: prop = attr.evolve(prop, nullable=parent.nullable) if isinstance(prop, EnumProperty): @@ -437,12 +450,14 @@ def _property_from_data( """Generate a Property from the OpenAPI dictionary representation of it""" name = utils.remove_string_escapes(name) if isinstance(data, oai.Reference): - return _property_from_ref(name=name, required=required, parent=None, data=data, schemas=schemas) + return _property_from_ref(name=name, required=required, parent=None, data=data, schemas=schemas, config=config) # A union of a single reference should just be passed through to that reference (don't create copy class) sub_data = (data.allOf or []) + data.anyOf + data.oneOf if len(sub_data) == 1 and isinstance(sub_data[0], oai.Reference): - return _property_from_ref(name=name, required=required, parent=data, data=sub_data[0], schemas=schemas) + return _property_from_ref( + name=name, required=required, parent=data, data=sub_data[0], schemas=schemas, config=config + ) if data.enum: return build_enum_property( @@ -459,7 +474,7 @@ def _property_from_data( data=data, name=name, required=required, schemas=schemas, parent_name=parent_name, config=config ) elif data.type == "string": - return _string_based_property(name=name, required=required, data=data), schemas + return _string_based_property(name=name, required=required, data=data, config=config), schemas elif data.type == "number": return ( FloatProperty( @@ -467,6 +482,7 @@ def _property_from_data( default=convert("float", data.default), required=required, nullable=data.nullable, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ), schemas, ) @@ -477,6 +493,7 @@ def _property_from_data( default=convert("int", data.default), required=required, nullable=data.nullable, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ), schemas, ) @@ -487,6 +504,7 @@ def _property_from_data( required=required, default=convert("bool", data.default), nullable=data.nullable, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ), schemas, ) @@ -499,7 +517,16 @@ def _property_from_data( data=data, name=name, schemas=schemas, required=required, parent_name=parent_name, config=config ) elif not data.type: - return AnyProperty(name=name, required=required, nullable=False, default=None), schemas + return ( + AnyProperty( + name=name, + required=required, + nullable=False, + default=None, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), + ), + schemas, + ) return PropertyError(data=data, detail=f"unknown type {data.type}"), schemas diff --git a/openapi_python_client/parser/properties/model_property.py b/openapi_python_client/parser/properties/model_property.py index 79ac48764..55c2b3cf1 100644 --- a/openapi_python_client/parser/properties/model_property.py +++ b/openapi_python_client/parser/properties/model_property.py @@ -210,6 +210,7 @@ def build_model_property( required=required, name=name, additional_properties=additional_properties, + python_name=utils.PythonIdentifier(value=name, prefix=config.field_prefix), ) if class_info.name in schemas.classes_by_name: error = PropertyError(data=data, detail=f'Attempted to generate duplicate models with name "{class_info.name}"') diff --git a/openapi_python_client/parser/properties/property.py b/openapi_python_client/parser/properties/property.py index d4ce54980..41e6a2b75 100644 --- a/openapi_python_client/parser/properties/property.py +++ b/openapi_python_client/parser/properties/property.py @@ -1,8 +1,11 @@ +__all__ = ["Property"] + from typing import ClassVar, Optional, Set import attr -from ... import utils +from ... import Config +from ...utils import PythonIdentifier @attr.s(auto_attribs=True, frozen=True) @@ -26,16 +29,13 @@ class Property: _type_string: ClassVar[str] = "" _json_type_string: ClassVar[str] = "" # Type of the property after JSON serialization default: Optional[str] = attr.ib() - python_name: str = attr.ib(init=False) + python_name: PythonIdentifier template: ClassVar[Optional[str]] = None json_is_dict: ClassVar[bool] = False - def __attrs_post_init__(self) -> None: - self.set_python_name(self.name) - - def set_python_name(self, new_name: str) -> None: - object.__setattr__(self, "python_name", utils.to_valid_python_identifier(utils.snake_case(new_name))) + def set_python_name(self, new_name: str, config: Config) -> None: + object.__setattr__(self, "python_name", PythonIdentifier(value=new_name, prefix=config.field_prefix)) def get_base_type_string(self) -> str: return self._type_string diff --git a/openapi_python_client/parser/responses.py b/openapi_python_client/parser/responses.py index cbda7a140..98300640d 100644 --- a/openapi_python_client/parser/responses.py +++ b/openapi_python_client/parser/responses.py @@ -6,6 +6,7 @@ from .. import Config from .. import schema as oai +from ..utils import PythonIdentifier from .errors import ParseError, PropertyError from .properties import AnyProperty, Property, Schemas, property_from_data @@ -27,7 +28,7 @@ class Response: } -def empty_response(status_code: int, response_name: str) -> Response: +def empty_response(*, status_code: int, response_name: str, config: Config) -> Response: """Return an untyped response, for when no response type is defined""" return Response( status_code=status_code, @@ -36,6 +37,7 @@ def empty_response(status_code: int, response_name: str) -> Response: default=None, nullable=False, required=True, + python_name=PythonIdentifier(value=response_name, prefix=config.field_prefix), ), source="None", ) @@ -49,7 +51,7 @@ def response_from_data( response_name = f"response_{status_code}" if isinstance(data, oai.Reference) or data.content is None: return ( - empty_response(status_code=status_code, response_name=response_name), + empty_response(status_code=status_code, response_name=response_name, config=config), schemas, ) @@ -64,7 +66,7 @@ def response_from_data( if schema_data is None: return ( - empty_response(status_code, response_name), + empty_response(status_code=status_code, response_name=response_name, config=config), schemas, ) diff --git a/openapi_python_client/utils.py b/openapi_python_client/utils.py index 8c9e436f5..a8e80d027 100644 --- a/openapi_python_client/utils.py +++ b/openapi_python_client/utils.py @@ -1,11 +1,25 @@ import builtins import re from keyword import iskeyword -from typing import List +from typing import Any, List delimiters = " _-" +class PythonIdentifier(str): + """A string which has been validated / transformed into a valid identifier for Python""" + + def __new__(cls, value: str, prefix: str) -> "PythonIdentifier": + new_value = fix_reserved_words(fix_keywords(snake_case(sanitize(value)))) + + if not new_value.isidentifier(): + new_value = f"{prefix}{new_value}" + return str.__new__(cls, new_value) + + def __deepcopy__(self, _: Any) -> "PythonIdentifier": + return self + + def sanitize(value: str) -> str: """Removes every character that isn't 0-9, A-Z, a-z, or a known delimiter""" return re.sub(rf"[^\w{delimiters}]+", "", value) @@ -55,23 +69,3 @@ def kebab_case(value: str) -> str: def remove_string_escapes(value: str) -> str: return value.replace('"', r"\"") - - -# This can be changed by config.Config.load_config -FIELD_PREFIX = "field_" - - -def to_valid_python_identifier(value: str) -> str: - """ - Given a string, attempt to coerce it into a valid Python identifier by stripping out invalid characters and, if - necessary, prepending a prefix. - - See: - https://docs.python.org/3/reference/lexical_analysis.html#identifiers - """ - new_value = fix_reserved_words(fix_keywords(sanitize(value))) - - if new_value.isidentifier(): - return new_value - - return f"{FIELD_PREFIX}{new_value}" diff --git a/poetry.lock b/poetry.lock index 9c826131e..b3da78953 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,3 +1,23 @@ +[[package]] +name = "anyio" +version = "3.2.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +category = "main" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +async-generator = {version = "*", markers = "python_version < \"3.7\""} +dataclasses = {version = "*", markers = "python_version < \"3.7\""} +idna = ">=2.8" +sniffio = ">=1.1" +typing-extensions = {version = "*", markers = "python_version < \"3.8\""} + +[package.extras] +doc = ["sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=6.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] +trio = ["trio (>=0.16)"] + [[package]] name = "appdirs" version = "1.4.4" @@ -24,11 +44,11 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "attrs" -version = "21.1.0" +version = "21.2.0" description = "Classes Without Boilerplate" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.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"] @@ -49,7 +69,7 @@ pyflakes = ">=1.1.0" [[package]] name = "black" -version = "21.4b1" +version = "21.6b0" description = "The uncompromising code formatter." category = "main" optional = false @@ -68,12 +88,13 @@ typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +d = ["aiohttp (>=3.6.0)", "aiohttp-cors (>=0.4.0)"] python2 = ["typed-ast (>=1.4.2)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2020.6.20" +version = "2021.5.30" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -81,11 +102,11 @@ python-versions = "*" [[package]] name = "chardet" -version = "3.0.4" +version = "4.0.0" description = "Universal encoding detector for Python 2 and 3" category = "dev" optional = false -python-versions = "*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "click" @@ -116,7 +137,7 @@ immutables = ">=0.9" [[package]] name = "coverage" -version = "5.3" +version = "5.5" description = "Code coverage measurement for Python" category = "dev" optional = false @@ -127,11 +148,11 @@ toml = ["toml"] [[package]] name = "dataclasses" -version = "0.6" +version = "0.8" description = "A backport of the dataclasses module for Python 3.6" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6, <3.7" [[package]] name = "dparse" @@ -151,7 +172,7 @@ pipenv = ["pipenv"] [[package]] name = "flake8" -version = "3.9.1" +version = "3.9.2" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false @@ -165,22 +186,23 @@ pyflakes = ">=2.3.0,<2.4.0" [[package]] name = "h11" -version = "0.9.0" +version = "0.12.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "httpcore" -version = "0.13.2" +version = "0.13.6" description = "A minimal low-level HTTP client." category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -h11 = "<1.0.0" +anyio = ">=3.0.0,<4.0.0" +h11 = ">=0.11,<0.13" sniffio = ">=1.0.0,<2.0.0" [package.extras] @@ -188,7 +210,7 @@ http2 = ["h2 (>=3,<5)"] [[package]] name = "httpx" -version = "0.18.1" +version = "0.18.2" description = "The next generation HTTP client." category = "main" optional = false @@ -197,7 +219,7 @@ python-versions = ">=3.6" [package.dependencies] async-generator = {version = "*", markers = "python_version < \"3.7\""} certifi = "*" -httpcore = ">=0.13.0,<0.14.0" +httpcore = ">=0.13.3,<0.14.0" rfc3986 = {version = ">=1.3,<2", extras = ["idna2008"]} sniffio = "*" @@ -215,15 +237,18 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "immutables" -version = "0.14" +version = "0.15" description = "Immutable Collections" category = "main" optional = false python-versions = ">=3.5" +[package.extras] +test = ["flake8 (>=3.8.4,<3.9.0)", "pycodestyle (>=2.6.0,<2.7.0)"] + [[package]] name = "importlib-metadata" -version = "2.0.0" +version = "2.1.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -234,11 +259,11 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "rst.linker"] -testing = ["packaging", "pep517", "importlib-resources (>=1.3)"] +testing = ["packaging", "pep517", "unittest2", "importlib-resources (>=1.3)"] [[package]] name = "iniconfig" -version = "1.0.1" +version = "1.1.1" description = "iniconfig: brain-dead simple config-ini parsing" category = "dev" optional = false @@ -246,34 +271,35 @@ python-versions = "*" [[package]] name = "isort" -version = "5.8.0" +version = "5.9.1" description = "A Python utility / library to sort Python imports." category = "main" optional = false -python-versions = ">=3.6,<4.0" +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.0" +version = "3.0.1" description = "A very fast and expressive template engine." category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -MarkupSafe = ">=2.0.0rc2" +MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] [[package]] name = "markupsafe" -version = "2.0.0" +version = "2.0.1" description = "Safely add untrusted strings to HTML/XML markup." category = "main" optional = false @@ -297,7 +323,7 @@ python-versions = ">=3.5" [[package]] name = "mypy" -version = "0.812" +version = "0.910" description = "Optional static typing for Python" category = "dev" optional = false @@ -305,11 +331,13 @@ python-versions = ">=3.5" [package.dependencies] mypy-extensions = ">=0.4.3,<0.5.0" -typed-ast = ">=1.4.0,<1.5.0" +toml = "*" +typed-ast = {version = ">=1.4.0,<1.5.0", markers = "python_version < \"3.8\""} typing-extensions = ">=3.7.4" [package.extras] dmypy = ["psutil (>=4.0)"] +python2 = ["typed-ast (>=1.4.0,<1.5.0)"] [[package]] name = "mypy-extensions" @@ -321,15 +349,14 @@ python-versions = "*" [[package]] name = "packaging" -version = "20.4" +version = "21.0" description = "Core utilities for Python packages" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [package.dependencies] pyparsing = ">=2.0.2" -six = "*" [[package]] name = "pathspec" @@ -355,7 +382,7 @@ dev = ["pre-commit", "tox"] [[package]] name = "psutil" -version = "5.7.2" +version = "5.8.0" description = "Cross-platform lib for process and system monitoring in Python." category = "dev" optional = false @@ -398,7 +425,7 @@ email = ["email-validator (>=1.0.3)"] [[package]] name = "pyflakes" -version = "2.3.0" +version = "2.3.1" description = "passive checker of Python programs" category = "main" optional = false @@ -414,7 +441,7 @@ python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "pytest" -version = "6.2.3" +version = "6.2.4" description = "pytest: simple powerful testing with Python" category = "dev" optional = false @@ -436,7 +463,7 @@ testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xm [[package]] name = "pytest-cov" -version = "2.11.1" +version = "2.12.1" description = "Pytest plugin for measuring coverage." category = "dev" optional = false @@ -445,13 +472,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] coverage = ">=5.2.1" pytest = ">=4.6" +toml = "*" [package.extras] -testing = ["fields", "hunter", "process-tests (==2.0.2)", "six", "pytest-xdist", "virtualenv"] +testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"] [[package]] name = "pytest-mock" -version = "3.6.0" +version = "3.6.1" description = "Thin-wrapper around the mock package for easier use with pytest" category = "dev" optional = false @@ -495,7 +523,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] name = "regex" -version = "2020.9.27" +version = "2021.7.1" description = "Alternative regular expression module, to replace re." category = "main" optional = false @@ -503,7 +531,7 @@ python-versions = "*" [[package]] name = "requests" -version = "2.24.0" +version = "2.25.1" description = "Python HTTP for Humans." category = "dev" optional = false @@ -511,9 +539,9 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] certifi = ">=2017.4.17" -chardet = ">=3.0.2,<4" +chardet = ">=3.0.2,<5" idna = ">=2.5,<3" -urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26" +urllib3 = ">=1.21.1,<1.27" [package.extras] security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"] @@ -557,7 +585,7 @@ python-versions = "!=3.0,!=3.1,!=3.2,!=3.3,>=2.6" [[package]] name = "six" -version = "1.15.0" +version = "1.16.0" description = "Python 2 and 3 compatibility utilities" category = "main" optional = false @@ -565,7 +593,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "sniffio" -version = "1.1.0" +version = "1.2.0" description = "Sniff out which async library your code is running under" category = "main" optional = false @@ -576,24 +604,25 @@ contextvars = {version = ">=2.1", markers = "python_version < \"3.7\""} [[package]] name = "taskipy" -version = "1.7.0" +version = "1.8.1" description = "tasks runner for python projects" category = "dev" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] +colorama = ">=0.4.4,<0.5.0" mslex = ">=0.3.0,<0.4.0" psutil = ">=5.7.2,<6.0.0" toml = ">=0.10.0,<0.11.0" [[package]] name = "toml" -version = "0.10.1" +version = "0.10.2" description = "Python Library for Tom's Obvious, Minimal Language" category = "main" optional = false -python-versions = "*" +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "typed-ast" @@ -633,9 +662,41 @@ colorama = ">=0.4.3,<0.5.0" shellingham = ">=1.3.2,<2.0.0" typer = ">=0.3.0,<0.4.0" +[[package]] +name = "types-certifi" +version = "0.1.4" +description = "Typing stubs for certifi" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-dataclasses" +version = "0.1.5" +description = "Typing stubs for dataclasses" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-python-dateutil" +version = "0.1.4" +description = "Typing stubs for python-dateutil" +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "types-pyyaml" +version = "5.4.3" +description = "Typing stubs for PyYAML" +category = "dev" +optional = false +python-versions = "*" + [[package]] name = "typing-extensions" -version = "3.7.4.3" +version = "3.10.0.0" description = "Backported and Experimental Type Hints for Python 3.5+" category = "main" optional = false @@ -643,7 +704,7 @@ python-versions = "*" [[package]] name = "urllib3" -version = "1.25.10" +version = "1.26.6" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "dev" optional = false @@ -651,27 +712,31 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" [package.extras] brotli = ["brotlipy (>=0.6.0)"] -secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] [[package]] name = "zipp" -version = "3.3.0" +version = "3.5.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false python-versions = ">=3.6" [package.extras] -docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "jaraco.test (>=3.2.0)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"] +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"] [metadata] lock-version = "1.1" python-versions = "^3.6.2" -content-hash = "50067d693474f578fd8e49dda4bb207e67b41497d22b44468379aed25c2602e9" +content-hash = "8290c09aa4a11f17c91f6fe5cedc5c6d9da7fc0edfc137a2b874d3de30430b03" [metadata.files] +anyio = [ + {file = "anyio-3.2.1-py3-none-any.whl", hash = "sha256:442678a3c7e1cdcdbc37dcfe4527aa851b1b0c9162653b516e9f509821691d50"}, + {file = "anyio-3.2.1.tar.gz", hash = "sha256:07968db9fa7c1ca5435a133dc62f988d84ef78e1d9b22814a59d1c62618afbc5"}, +] appdirs = [ {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, @@ -685,23 +750,23 @@ atomicwrites = [ {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, ] attrs = [ - {file = "attrs-21.1.0-py2.py3-none-any.whl", hash = "sha256:8ee1e5f5a1afc5b19bdfae4fdf0c35ed324074bdce3500c939842c8f818645d9"}, - {file = "attrs-21.1.0.tar.gz", hash = "sha256:3901be1cb7c2a780f14668691474d9252c070a756be0a9ead98cfeabfa11aeb8"}, + {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, + {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] autoflake = [ {file = "autoflake-1.4.tar.gz", hash = "sha256:61a353012cff6ab94ca062823d1fb2f692c4acda51c76ff83a8d77915fba51ea"}, ] black = [ - {file = "black-21.4b1-py3-none-any.whl", hash = "sha256:c9601dc863779db2fb1bf18b345bbfa2bb868463123cde6a7eff44b59e4ef739"}, - {file = "black-21.4b1.tar.gz", hash = "sha256:20d326de75d13be6290925a95c94a9f368aca2f71cb7b753a938a5ae20f34a37"}, + {file = "black-21.6b0-py3-none-any.whl", hash = "sha256:dfb8c5a069012b2ab1e972e7b908f5fb42b6bbabcba0a788b86dc05067c7d9c7"}, + {file = "black-21.6b0.tar.gz", hash = "sha256:dc132348a88d103016726fe360cb9ede02cecf99b76e3660ce6c596be132ce04"}, ] certifi = [ - {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"}, - {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"}, + {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, + {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, ] chardet = [ - {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"}, - {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"}, + {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"}, + {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, @@ -715,134 +780,155 @@ contextvars = [ {file = "contextvars-2.4.tar.gz", hash = "sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"}, ] coverage = [ - {file = "coverage-5.3-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270"}, - {file = "coverage-5.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9"}, - {file = "coverage-5.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729"}, - {file = "coverage-5.3-cp27-cp27m-win32.whl", hash = "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d"}, - {file = "coverage-5.3-cp27-cp27m-win_amd64.whl", hash = "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9"}, - {file = "coverage-5.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5"}, - {file = "coverage-5.3-cp35-cp35m-macosx_10_13_x86_64.whl", hash = "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097"}, - {file = "coverage-5.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9"}, - {file = "coverage-5.3-cp35-cp35m-win32.whl", hash = "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636"}, - {file = "coverage-5.3-cp35-cp35m-win_amd64.whl", hash = "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f"}, - {file = "coverage-5.3-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54"}, - {file = "coverage-5.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7"}, - {file = "coverage-5.3-cp36-cp36m-win32.whl", hash = "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a"}, - {file = "coverage-5.3-cp36-cp36m-win_amd64.whl", hash = "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d"}, - {file = "coverage-5.3-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f"}, - {file = "coverage-5.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c"}, - {file = "coverage-5.3-cp37-cp37m-win32.whl", hash = "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751"}, - {file = "coverage-5.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709"}, - {file = "coverage-5.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f"}, - {file = "coverage-5.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259"}, - {file = "coverage-5.3-cp38-cp38-win32.whl", hash = "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82"}, - {file = "coverage-5.3-cp38-cp38-win_amd64.whl", hash = "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221"}, - {file = "coverage-5.3-cp39-cp39-macosx_10_13_x86_64.whl", hash = "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21"}, - {file = "coverage-5.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24"}, - {file = "coverage-5.3-cp39-cp39-win32.whl", hash = "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7"}, - {file = "coverage-5.3-cp39-cp39-win_amd64.whl", hash = "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7"}, - {file = "coverage-5.3.tar.gz", hash = "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0"}, + {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, + {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, + {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, + {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, + {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, + {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, + {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, + {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, + {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, + {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, + {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, + {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, + {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, + {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, + {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, + {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, + {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, + {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, + {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, + {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, + {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, + {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, + {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, + {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, + {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, + {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, + {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, + {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, + {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, + {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, + {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, + {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, + {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, + {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, + {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, + {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, + {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, ] dataclasses = [ - {file = "dataclasses-0.6-py3-none-any.whl", hash = "sha256:454a69d788c7fda44efd71e259be79577822f5e3f53f029a22d08004e951dc9f"}, - {file = "dataclasses-0.6.tar.gz", hash = "sha256:6988bd2b895eef432d562370bb707d540f32f7360ab13da45340101bc2307d84"}, + {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, + {file = "dataclasses-0.8.tar.gz", hash = "sha256:8479067f342acf957dc82ec415d355ab5edb7e7646b90dc6e2fd1d96ad084c97"}, ] dparse = [ {file = "dparse-0.5.1-py3-none-any.whl", hash = "sha256:e953a25e44ebb60a5c6efc2add4420c177f1d8404509da88da9729202f306994"}, {file = "dparse-0.5.1.tar.gz", hash = "sha256:a1b5f169102e1c894f9a7d5ccf6f9402a836a5d24be80a986c7ce9eaed78f367"}, ] flake8 = [ - {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"}, - {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"}, + {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, + {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, ] h11 = [ - {file = "h11-0.9.0-py2.py3-none-any.whl", hash = "sha256:4bc6d6a1238b7615b266ada57e0618568066f57dd6fa967d1290ec9309b2f2f1"}, - {file = "h11-0.9.0.tar.gz", hash = "sha256:33d4bca7be0fa039f4e84d50ab00531047e53d6ee8ffbc83501ea602c169cae1"}, + {file = "h11-0.12.0-py3-none-any.whl", hash = "sha256:36a3cb8c0a032f56e2da7084577878a035d3b61d104230d4bd49c0c6b555a9c6"}, + {file = "h11-0.12.0.tar.gz", hash = "sha256:47222cb6067e4a307d535814917cd98fd0a57b6788ce715755fa2b6c28b56042"}, ] httpcore = [ - {file = "httpcore-0.13.2-py3-none-any.whl", hash = "sha256:52b7d9413f6f5592a667de9209d70d4d41aba3fb0540dd7c93475c78b85941e9"}, - {file = "httpcore-0.13.2.tar.gz", hash = "sha256:c16efbdf643e1b57bde0adc12c53b08645d7d92d6d345a3f71adfc2a083e7fd2"}, + {file = "httpcore-0.13.6-py3-none-any.whl", hash = "sha256:db4c0dcb8323494d01b8c6d812d80091a31e520033e7b0120883d6f52da649ff"}, + {file = "httpcore-0.13.6.tar.gz", hash = "sha256:b0d16f0012ec88d8cc848f5a55f8a03158405f4bca02ee49bc4ca2c1fda49f3e"}, ] httpx = [ - {file = "httpx-0.18.1-py3-none-any.whl", hash = "sha256:ad2e3db847be736edc4b272c4d5788790a7e5789ef132fc6b5fef8aeb9e9f6e0"}, - {file = "httpx-0.18.1.tar.gz", hash = "sha256:0a2651dd2b9d7662c70d12ada5c290abcf57373b9633515fe4baa9f62566086f"}, + {file = "httpx-0.18.2-py3-none-any.whl", hash = "sha256:979afafecb7d22a1d10340bafb403cf2cb75aff214426ff206521fc79d26408c"}, + {file = "httpx-0.18.2.tar.gz", hash = "sha256:9f99c15d33642d38bce8405df088c1c4cfd940284b4290cacbfb02e64f4877c6"}, ] idna = [ {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"}, {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"}, ] immutables = [ - {file = "immutables-0.14-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:860666fab142401a5535bf65cbd607b46bc5ed25b9d1eb053ca8ed9a1a1a80d6"}, - {file = "immutables-0.14-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:ce01788878827c3f0331c254a4ad8d9721489a5e65cc43e19c80040b46e0d297"}, - {file = "immutables-0.14-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8797eed4042f4626b0bc04d9cf134208918eb0c937a8193a2c66df5041e62d2e"}, - {file = "immutables-0.14-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:33ce2f977da7b5e0dddd93744862404bdb316ffe5853ec853e53141508fa2e6a"}, - {file = "immutables-0.14-cp36-cp36m-win_amd64.whl", hash = "sha256:6c8eace4d98988c72bcb37c05e79aae756832738305ae9497670482a82db08bc"}, - {file = "immutables-0.14-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:ab6c18b7b2b2abc83e0edc57b0a38bf0915b271582a1eb8c7bed1c20398f8040"}, - {file = "immutables-0.14-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:c099212fd6504513a50e7369fe281007c820cf9d7bb22a336486c63d77d6f0b2"}, - {file = "immutables-0.14-cp37-cp37m-win_amd64.whl", hash = "sha256:714aedbdeba4439d91cb5e5735cb10631fc47a7a69ea9cc8ecbac90322d50a4a"}, - {file = "immutables-0.14-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:1c11050c49e193a1ec9dda1747285333f6ba6a30bbeb2929000b9b1192097ec0"}, - {file = "immutables-0.14-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:c453e12b95e1d6bb4909e8743f88b7f5c0c97b86a8bc0d73507091cb644e3c1e"}, - {file = "immutables-0.14-cp38-cp38-win_amd64.whl", hash = "sha256:ef9da20ec0f1c5853b5c8f8e3d9e1e15b8d98c259de4b7515d789a606af8745e"}, - {file = "immutables-0.14.tar.gz", hash = "sha256:a0a1cc238b678455145bae291d8426f732f5255537ed6a5b7645949704c70a78"}, + {file = "immutables-0.15-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:6728f4392e3e8e64b593a5a0cd910a1278f07f879795517e09f308daed138631"}, + {file = "immutables-0.15-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f0836cd3bdc37c8a77b192bbe5f41dbcc3ce654db048ebbba89bdfe6db7a1c7a"}, + {file = "immutables-0.15-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:8703d8abfd8687932f2a05f38e7de270c3a6ca3bd1c1efb3c938656b3f2f985a"}, + {file = "immutables-0.15-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:b8ad986f9b532c026f19585289384b0769188fcb68b37c7f0bd0df9092a6ca54"}, + {file = "immutables-0.15-cp36-cp36m-win_amd64.whl", hash = "sha256:6f117d9206165b9dab8fd81c5129db757d1a044953f438654236ed9a7a4224ae"}, + {file = "immutables-0.15-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b75ade826920c4e490b1bb14cf967ac14e61eb7c5562161c5d7337d61962c226"}, + {file = "immutables-0.15-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:b7e13c061785e34f73c4f659861f1b3e4a5fd918e4395c84b21c4e3d449ebe27"}, + {file = "immutables-0.15-cp37-cp37m-win_amd64.whl", hash = "sha256:3035849accee4f4e510ed7c94366a40e0f5fef9069fbe04a35f4787b13610a4a"}, + {file = "immutables-0.15-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:b04fa69174e0c8f815f9c55f2a43fc9e5a68452fab459a08e904a74e8471639f"}, + {file = "immutables-0.15-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:141c2e9ea515a3a815007a429f0b47a578ebeb42c831edaec882a245a35fffca"}, + {file = "immutables-0.15-cp38-cp38-win_amd64.whl", hash = "sha256:cbe8c64640637faa5535d539421b293327f119c31507c33ca880bd4f16035eb6"}, + {file = "immutables-0.15-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:a0a4e4417d5ef4812d7f99470cd39347b58cb927365dd2b8da9161040d260db0"}, + {file = "immutables-0.15-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3b15c08c71c59e5b7c2470ef949d49ff9f4263bb77f488422eaa157da84d6999"}, + {file = "immutables-0.15-cp39-cp39-win_amd64.whl", hash = "sha256:2283a93c151566e6830aee0e5bee55fc273455503b43aa004356b50f9182092b"}, + {file = "immutables-0.15.tar.gz", hash = "sha256:3713ab1ebbb6946b7ce1387bb9d1d7f5e09c45add58c2a2ee65f963c171e746b"}, ] importlib-metadata = [ - {file = "importlib_metadata-2.0.0-py2.py3-none-any.whl", hash = "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"}, - {file = "importlib_metadata-2.0.0.tar.gz", hash = "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da"}, + {file = "importlib_metadata-2.1.1-py2.py3-none-any.whl", hash = "sha256:c2d6341ff566f609e89a2acb2db190e5e1d23d5409d6cc8d2fe34d72443876d4"}, + {file = "importlib_metadata-2.1.1.tar.gz", hash = "sha256:b8de9eff2b35fb037368f28a7df1df4e6436f578fa74423505b6c6a778d5b5dd"}, ] iniconfig = [ - {file = "iniconfig-1.0.1-py3-none-any.whl", hash = "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437"}, - {file = "iniconfig-1.0.1.tar.gz", hash = "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"}, + {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, + {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, ] isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, + {file = "isort-5.9.1-py3-none-any.whl", hash = "sha256:8e2c107091cfec7286bc0f68a547d0ba4c094d460b732075b6fba674f1035c0c"}, + {file = "isort-5.9.1.tar.gz", hash = "sha256:83510593e07e433b77bd5bff0f6f607dbafa06d1a89022616f02d8b699cfcd56"}, ] jinja2 = [ - {file = "Jinja2-3.0.0-py3-none-any.whl", hash = "sha256:2f2de5285cf37f33d33ecd4a9080b75c87cd0c1994d5a9c6df17131ea1f049c6"}, - {file = "Jinja2-3.0.0.tar.gz", hash = "sha256:ea8d7dd814ce9df6de6a761ec7f1cac98afe305b8cdc4aaae4e114b8d8ce24c5"}, + {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, + {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, ] markupsafe = [ - {file = "MarkupSafe-2.0.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2efaeb1baff547063bad2b2893a8f5e9c459c4624e1a96644bbba08910ae34e0"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:441ce2a8c17683d97e06447fcbccbdb057cbf587c78eb75ae43ea7858042fe2c"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:45535241baa0fc0ba2a43961a1ac7562ca3257f46c4c3e9c0de38b722be41bd1"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:90053234a6479738fd40d155268af631c7fca33365f964f2208867da1349294b"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3b54a9c68995ef4164567e2cd1a5e16db5dac30b2a50c39c82db8d4afaf14f63"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:f58b5ba13a5689ca8317b98439fccfbcc673acaaf8241c1869ceea40f5d585bf"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-win32.whl", hash = "sha256:a00dce2d96587651ef4fa192c17e039e8cfab63087c67e7d263a5533c7dad715"}, - {file = "MarkupSafe-2.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:007dc055dbce5b1104876acee177dbfd18757e19d562cd440182e1f492e96b95"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a08cd07d3c3c17cd33d9e66ea9dee8f8fc1c48e2d11bd88fd2dc515a602c709b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:3c352ff634e289061711608f5e474ec38dbaa21e3e168820d53d5f4015e5b91b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:32200f562daaab472921a11cbb63780f1654552ae49518196fc361ed8e12e901"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:fef86115fdad7ae774720d7103aa776144cf9b66673b4afa9bcaa7af990ed07b"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:e79212d09fc0e224d20b43ad44bb0a0a3416d1e04cf6b45fed265114a5d43d20"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:79b2ae94fa991be023832e6bcc00f41dbc8e5fe9d997a02db965831402551730"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-win32.whl", hash = "sha256:3261fae28155e5c8634dd7710635fe540a05b58f160cef7713c7700cb9980e66"}, - {file = "MarkupSafe-2.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e4570d16f88c7f3032ed909dc9e905a17da14a1c4cfd92608e3fda4cb1208bbd"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f806bfd0f218477d7c46a11d3e52dc7f5fdfaa981b18202b7dc84bbc287463b"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e77e4b983e2441aff0c0d07ee711110c106b625f440292dfe02a2f60c8218bd6"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:031bf79a27d1c42f69c276d6221172417b47cb4b31cdc73d362a9bf5a1889b9f"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:83cf0228b2f694dcdba1374d5312f2277269d798e65f40344964f642935feac1"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:4cc563836f13c57f1473bc02d1e01fc37bab70ad4ee6be297d58c1d66bc819bf"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d00a669e4a5bec3ee6dbeeeedd82a405ced19f8aeefb109a012ea88a45afff96"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-win32.whl", hash = "sha256:161d575fa49395860b75da5135162481768b11208490d5a2143ae6785123e77d"}, - {file = "MarkupSafe-2.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:58bc9fce3e1557d463ef5cee05391a05745fd95ed660f23c1742c711712c0abb"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3fb47f97f1d338b943126e90b79cad50d4fcfa0b80637b5a9f468941dbbd9ce5"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dab0c685f21f4a6c95bfc2afd1e7eae0033b403dd3d8c1b6d13a652ada75b348"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_i686.whl", hash = "sha256:664832fb88b8162268928df233f4b12a144a0c78b01d38b81bdcf0fc96668ecb"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:df561f65049ed3556e5b52541669310e88713fdae2934845ec3606f283337958"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:24bbc3507fb6dfff663af7900a631f2aca90d5a445f272db5fc84999fa5718bc"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:87de598edfa2230ff274c4de7fcf24c73ffd96208c8e1912d5d0fee459767d75"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a19d39b02a24d3082856a5b06490b714a9d4179321225bbf22809ff1e1887cc8"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-win32.whl", hash = "sha256:4aca81a687975b35e3e80bcf9aa93fe10cd57fac37bf18b2314c186095f57e05"}, - {file = "MarkupSafe-2.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:70820a1c96311e02449591cbdf5cd1c6a34d5194d5b55094ab725364375c9eb2"}, - {file = "MarkupSafe-2.0.0.tar.gz", hash = "sha256:4fae0677f712ee090721d8b17f412f1cbceefbf0dc180fe91bab3232f38b4527"}, + {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"}, ] mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, @@ -853,36 +939,37 @@ mslex = [ {file = "mslex-0.3.0.tar.gz", hash = "sha256:4a1ac3f25025cad78ad2fe499dd16d42759f7a3801645399cce5c404415daa97"}, ] mypy = [ - {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"}, - {file = "mypy-0.812-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:28fb5479c494b1bab244620685e2eb3c3f988d71fd5d64cc753195e8ed53df7c"}, - {file = "mypy-0.812-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:9743c91088d396c1a5a3c9978354b61b0382b4e3c440ce83cf77994a43e8c521"}, - {file = "mypy-0.812-cp35-cp35m-win_amd64.whl", hash = "sha256:d7da2e1d5f558c37d6e8c1246f1aec1e7349e4913d8fb3cb289a35de573fe2eb"}, - {file = "mypy-0.812-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4eec37370483331d13514c3f55f446fc5248d6373e7029a29ecb7b7494851e7a"}, - {file = "mypy-0.812-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d65cc1df038ef55a99e617431f0553cd77763869eebdf9042403e16089fe746c"}, - {file = "mypy-0.812-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:61a3d5b97955422964be6b3baf05ff2ce7f26f52c85dd88db11d5e03e146a3a6"}, - {file = "mypy-0.812-cp36-cp36m-win_amd64.whl", hash = "sha256:25adde9b862f8f9aac9d2d11971f226bd4c8fbaa89fb76bdadb267ef22d10064"}, - {file = "mypy-0.812-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:552a815579aa1e995f39fd05dde6cd378e191b063f031f2acfe73ce9fb7f9e56"}, - {file = "mypy-0.812-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:499c798053cdebcaa916eef8cd733e5584b5909f789de856b482cd7d069bdad8"}, - {file = "mypy-0.812-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:5873888fff1c7cf5b71efbe80e0e73153fe9212fafdf8e44adfe4c20ec9f82d7"}, - {file = "mypy-0.812-cp37-cp37m-win_amd64.whl", hash = "sha256:9f94aac67a2045ec719ffe6111df543bac7874cee01f41928f6969756e030564"}, - {file = "mypy-0.812-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d23e0ea196702d918b60c8288561e722bf437d82cb7ef2edcd98cfa38905d506"}, - {file = "mypy-0.812-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:674e822aa665b9fd75130c6c5f5ed9564a38c6cea6a6432ce47eafb68ee578c5"}, - {file = "mypy-0.812-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:abf7e0c3cf117c44d9285cc6128856106183938c68fd4944763003decdcfeb66"}, - {file = "mypy-0.812-cp38-cp38-win_amd64.whl", hash = "sha256:0d0a87c0e7e3a9becdfbe936c981d32e5ee0ccda3e0f07e1ef2c3d1a817cf73e"}, - {file = "mypy-0.812-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7ce3175801d0ae5fdfa79b4f0cfed08807af4d075b402b7e294e6aa72af9aa2a"}, - {file = "mypy-0.812-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:b09669bcda124e83708f34a94606e01b614fa71931d356c1f1a5297ba11f110a"}, - {file = "mypy-0.812-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:33f159443db0829d16f0a8d83d94df3109bb6dd801975fe86bacb9bf71628e97"}, - {file = "mypy-0.812-cp39-cp39-win_amd64.whl", hash = "sha256:3f2aca7f68580dc2508289c729bd49ee929a436208d2b2b6aab15745a70a57df"}, - {file = "mypy-0.812-py3-none-any.whl", hash = "sha256:2f9b3407c58347a452fc0736861593e105139b905cca7d097e413453a1d650b4"}, - {file = "mypy-0.812.tar.gz", hash = "sha256:cd07039aa5df222037005b08fbbfd69b3ab0b0bd7a07d7906de75ae52c4e3119"}, + {file = "mypy-0.910-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a155d80ea6cee511a3694b108c4494a39f42de11ee4e61e72bc424c490e46457"}, + {file = "mypy-0.910-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:b94e4b785e304a04ea0828759172a15add27088520dc7e49ceade7834275bedb"}, + {file = "mypy-0.910-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:088cd9c7904b4ad80bec811053272986611b84221835e079be5bcad029e79dd9"}, + {file = "mypy-0.910-cp35-cp35m-win_amd64.whl", hash = "sha256:adaeee09bfde366d2c13fe6093a7df5df83c9a2ba98638c7d76b010694db760e"}, + {file = "mypy-0.910-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ecd2c3fe726758037234c93df7e98deb257fd15c24c9180dacf1ef829da5f921"}, + {file = "mypy-0.910-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d9dd839eb0dc1bbe866a288ba3c1afc33a202015d2ad83b31e875b5905a079b6"}, + {file = "mypy-0.910-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:3e382b29f8e0ccf19a2df2b29a167591245df90c0b5a2542249873b5c1d78212"}, + {file = "mypy-0.910-cp36-cp36m-win_amd64.whl", hash = "sha256:53fd2eb27a8ee2892614370896956af2ff61254c275aaee4c230ae771cadd885"}, + {file = "mypy-0.910-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b6fb13123aeef4a3abbcfd7e71773ff3ff1526a7d3dc538f3929a49b42be03f0"}, + {file = "mypy-0.910-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:e4dab234478e3bd3ce83bac4193b2ecd9cf94e720ddd95ce69840273bf44f6de"}, + {file = "mypy-0.910-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:7df1ead20c81371ccd6091fa3e2878559b5c4d4caadaf1a484cf88d93ca06703"}, + {file = "mypy-0.910-cp37-cp37m-win_amd64.whl", hash = "sha256:0aadfb2d3935988ec3815952e44058a3100499f5be5b28c34ac9d79f002a4a9a"}, + {file = "mypy-0.910-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ec4e0cd079db280b6bdabdc807047ff3e199f334050db5cbb91ba3e959a67504"}, + {file = "mypy-0.910-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:119bed3832d961f3a880787bf621634ba042cb8dc850a7429f643508eeac97b9"}, + {file = "mypy-0.910-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:866c41f28cee548475f146aa4d39a51cf3b6a84246969f3759cb3e9c742fc072"}, + {file = "mypy-0.910-cp38-cp38-win_amd64.whl", hash = "sha256:ceb6e0a6e27fb364fb3853389607cf7eb3a126ad335790fa1e14ed02fba50811"}, + {file = "mypy-0.910-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a85e280d4d217150ce8cb1a6dddffd14e753a4e0c3cf90baabb32cefa41b59e"}, + {file = "mypy-0.910-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:42c266ced41b65ed40a282c575705325fa7991af370036d3f134518336636f5b"}, + {file = "mypy-0.910-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3c4b8ca36877fc75339253721f69603a9c7fdb5d4d5a95a1a1b899d8b86a4de2"}, + {file = "mypy-0.910-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c0df2d30ed496a08de5daed2a9ea807d07c21ae0ab23acf541ab88c24b26ab97"}, + {file = "mypy-0.910-cp39-cp39-win_amd64.whl", hash = "sha256:c6c2602dffb74867498f86e6129fd52a2770c48b7cd3ece77ada4fa38f94eba8"}, + {file = "mypy-0.910-py3-none-any.whl", hash = "sha256:ef565033fa5a958e62796867b1df10c40263ea9ded87164d67572834e57a174d"}, + {file = "mypy-0.910.tar.gz", hash = "sha256:704098302473cb31a218f1775a873b376b30b4c18229421e9e9dc8916fd16150"}, ] 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"}, ] packaging = [ - {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"}, - {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"}, + {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, + {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, ] pathspec = [ {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, @@ -893,17 +980,34 @@ pluggy = [ {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"}, ] psutil = [ - {file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"}, - {file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"}, - {file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"}, - {file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"}, - {file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"}, - {file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"}, - {file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"}, - {file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"}, - {file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"}, - {file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"}, - {file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"}, + {file = "psutil-5.8.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:0066a82f7b1b37d334e68697faba68e5ad5e858279fd6351c8ca6024e8d6ba64"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:0ae6f386d8d297177fd288be6e8d1afc05966878704dad9847719650e44fc49c"}, + {file = "psutil-5.8.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:12d844996d6c2b1d3881cfa6fa201fd635971869a9da945cf6756105af73d2df"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:02b8292609b1f7fcb34173b25e48d0da8667bc85f81d7476584d889c6e0f2131"}, + {file = "psutil-5.8.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6ffe81843131ee0ffa02c317186ed1e759a145267d54fdef1bc4ea5f5931ab60"}, + {file = "psutil-5.8.0-cp27-none-win32.whl", hash = "sha256:ea313bb02e5e25224e518e4352af4bf5e062755160f77e4b1767dd5ccb65f876"}, + {file = "psutil-5.8.0-cp27-none-win_amd64.whl", hash = "sha256:5da29e394bdedd9144c7331192e20c1f79283fb03b06e6abd3a8ae45ffecee65"}, + {file = "psutil-5.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:74fb2557d1430fff18ff0d72613c5ca30c45cdbfcddd6a5773e9fc1fe9364be8"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:74f2d0be88db96ada78756cb3a3e1b107ce8ab79f65aa885f76d7664e56928f6"}, + {file = "psutil-5.8.0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99de3e8739258b3c3e8669cb9757c9a861b2a25ad0955f8e53ac662d66de61ac"}, + {file = "psutil-5.8.0-cp36-cp36m-win32.whl", hash = "sha256:36b3b6c9e2a34b7d7fbae330a85bf72c30b1c827a4366a07443fc4b6270449e2"}, + {file = "psutil-5.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:52de075468cd394ac98c66f9ca33b2f54ae1d9bff1ef6b67a212ee8f639ec06d"}, + {file = "psutil-5.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a5fd10ce6b6344e616cf01cc5b849fa8103fbb5ba507b6b2dee4c11e84c935"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:61f05864b42fedc0771d6d8e49c35f07efd209ade09a5afe6a5059e7bb7bf83d"}, + {file = "psutil-5.8.0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:0dd4465a039d343925cdc29023bb6960ccf4e74a65ad53e768403746a9207023"}, + {file = "psutil-5.8.0-cp37-cp37m-win32.whl", hash = "sha256:1bff0d07e76114ec24ee32e7f7f8d0c4b0514b3fae93e3d2aaafd65d22502394"}, + {file = "psutil-5.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:fcc01e900c1d7bee2a37e5d6e4f9194760a93597c97fee89c4ae51701de03563"}, + {file = "psutil-5.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6223d07a1ae93f86451d0198a0c361032c4c93ebd4bf6d25e2fb3edfad9571ef"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d225cd8319aa1d3c85bf195c4e07d17d3cd68636b8fc97e6cf198f782f99af28"}, + {file = "psutil-5.8.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:28ff7c95293ae74bf1ca1a79e8805fcde005c18a122ca983abf676ea3466362b"}, + {file = "psutil-5.8.0-cp38-cp38-win32.whl", hash = "sha256:ce8b867423291cb65cfc6d9c4955ee9bfc1e21fe03bb50e177f2b957f1c2469d"}, + {file = "psutil-5.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:90f31c34d25b1b3ed6c40cdd34ff122b1887a825297c017e4cbd6796dd8b672d"}, + {file = "psutil-5.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6323d5d845c2785efb20aded4726636546b26d3b577aded22492908f7c1bdda7"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:245b5509968ac0bd179287d91210cd3f37add77dad385ef238b275bad35fa1c4"}, + {file = "psutil-5.8.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:90d4091c2d30ddd0a03e0b97e6a33a48628469b99585e2ad6bf21f17423b112b"}, + {file = "psutil-5.8.0-cp39-cp39-win32.whl", hash = "sha256:ea372bcc129394485824ae3e3ddabe67dc0b118d262c568b4d2602a7070afdb0"}, + {file = "psutil-5.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:f4634b033faf0d968bb9220dd1c793b897ab7f1189956e1aa9eae752527127d3"}, + {file = "psutil-5.8.0.tar.gz", hash = "sha256:0c9ccb99ab76025f2f0bbecf341d4656e9c1351db8cc8a03ccd62e318ab4b5c6"}, ] py = [ {file = "py-1.10.0-py2.py3-none-any.whl", hash = "sha256:3b80836aa6d1feeaa108e046da6423ab8f6ceda6468545ae8d02d9d58d18818a"}, @@ -938,24 +1042,24 @@ pydantic = [ {file = "pydantic-1.8.2.tar.gz", hash = "sha256:26464e57ccaafe72b7ad156fdaa4e9b9ef051f69e175dbbb463283000c05ab7b"}, ] pyflakes = [ - {file = "pyflakes-2.3.0-py2.py3-none-any.whl", hash = "sha256:910208209dcea632721cb58363d0f72913d9e8cf64dc6f8ae2e02a3609aba40d"}, - {file = "pyflakes-2.3.0.tar.gz", hash = "sha256:e59fd8e750e588358f1b8885e5a4751203a0516e0ee6d34811089ac294c8806f"}, + {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, + {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, ] pyparsing = [ {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, ] pytest = [ - {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"}, - {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"}, + {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"}, + {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"}, ] pytest-cov = [ - {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"}, - {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"}, + {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"}, + {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"}, ] pytest-mock = [ - {file = "pytest-mock-3.6.0.tar.gz", hash = "sha256:f7c3d42d6287f4e45846c8231c31902b6fa2bea98735af413a43da4cf5b727f1"}, - {file = "pytest_mock-3.6.0-py3-none-any.whl", hash = "sha256:952139a535b5b48ac0bb2f90b5dd36b67c7e1ba92601f3a8012678c4bd7f0bcc"}, + {file = "pytest-mock-3.6.1.tar.gz", hash = "sha256:40217a058c52a63f1042f0784f62009e976ba824c418cced42e88d5f40ab0e62"}, + {file = "pytest_mock-3.6.1-py3-none-any.whl", hash = "sha256:30c2f2cc9759e76eee674b81ea28c9f0b94f8f0445a1b87762cadf774f0df7e3"}, ] python-dateutil = [ {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"}, @@ -996,37 +1100,47 @@ pyyaml = [ {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] regex = [ - {file = "regex-2020.9.27-cp27-cp27m-win32.whl", hash = "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3"}, - {file = "regex-2020.9.27-cp27-cp27m-win_amd64.whl", hash = "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19"}, - {file = "regex-2020.9.27-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be"}, - {file = "regex-2020.9.27-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b"}, - {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc"}, - {file = "regex-2020.9.27-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b"}, - {file = "regex-2020.9.27-cp36-cp36m-win32.whl", hash = "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63"}, - {file = "regex-2020.9.27-cp36-cp36m-win_amd64.whl", hash = "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b"}, - {file = "regex-2020.9.27-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7"}, - {file = "regex-2020.9.27-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc"}, - {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c"}, - {file = "regex-2020.9.27-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100"}, - {file = "regex-2020.9.27-cp37-cp37m-win32.whl", hash = "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707"}, - {file = "regex-2020.9.27-cp37-cp37m-win_amd64.whl", hash = "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab"}, - {file = "regex-2020.9.27-cp38-cp38-manylinux1_i686.whl", hash = "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef"}, - {file = "regex-2020.9.27-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121"}, - {file = "regex-2020.9.27-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b"}, - {file = "regex-2020.9.27-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637"}, - {file = "regex-2020.9.27-cp38-cp38-win32.whl", hash = "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"}, - {file = "regex-2020.9.27-cp38-cp38-win_amd64.whl", hash = "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c"}, - {file = "regex-2020.9.27-cp39-cp39-manylinux1_i686.whl", hash = "sha256:84cada8effefe9a9f53f9b0d2ba9b7b6f5edf8d2155f9fdbe34616e06ececf81"}, - {file = "regex-2020.9.27-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:816064fc915796ea1f26966163f6845de5af78923dfcecf6551e095f00983650"}, - {file = "regex-2020.9.27-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:5d892a4f1c999834eaa3c32bc9e8b976c5825116cde553928c4c8e7e48ebda67"}, - {file = "regex-2020.9.27-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:c9443124c67b1515e4fe0bb0aa18df640965e1030f468a2a5dc2589b26d130ad"}, - {file = "regex-2020.9.27-cp39-cp39-win32.whl", hash = "sha256:49f23ebd5ac073765ecbcf046edc10d63dcab2f4ae2bce160982cb30df0c0302"}, - {file = "regex-2020.9.27-cp39-cp39-win_amd64.whl", hash = "sha256:3d20024a70b97b4f9546696cbf2fd30bae5f42229fbddf8661261b1eaff0deb7"}, - {file = "regex-2020.9.27.tar.gz", hash = "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d"}, + {file = "regex-2021.7.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:494d0172774dc0beeea984b94c95389143db029575f7ca908edd74469321ea99"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:8cf6728f89b071bd3ab37cb8a0e306f4de897553a0ed07442015ee65fbf53d62"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1806370b2bef4d4193eebe8ee59a9fd7547836a34917b7badbe6561a8594d9cb"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d0cf2651a8804f6325747c7e55e3be0f90ee2848e25d6b817aa2728d263f9abb"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:268fe9dd1deb4a30c8593cabd63f7a241dfdc5bd9dd0233906c718db22cdd49a"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:7743798dfb573d006f1143d745bf17efad39775a5190b347da5d83079646be56"}, + {file = "regex-2021.7.1-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:0e46c1191b2eb293a6912269ed08b4512e7e241bbf591f97e527492e04c77e93"}, + {file = "regex-2021.7.1-cp36-cp36m-win32.whl", hash = "sha256:b1dbeef938281f240347d50f28ae53c4b046a23389cd1fc4acec5ea0eae646a1"}, + {file = "regex-2021.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:6c72ebb72e64e9bd195cb35a9b9bbfb955fd953b295255b8ae3e4ad4a146b615"}, + {file = "regex-2021.7.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf819c5b77ff44accc9a24e31f1f7ceaaf6c960816913ed3ef8443b9d20d81b6"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e80d2851109e56420b71f9702ad1646e2f0364528adbf6af85527bc61e49f394"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:a1b6a3f600d6aff97e3f28c34192c9ed93fee293bd96ef327b64adb51a74b2f6"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:ed77b97896312bc2deafe137ca2626e8b63808f5bedb944f73665c68093688a7"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a548bb51c4476332ce4139df8e637386730f79a92652a907d12c696b6252b64d"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:210c359e6ee5b83f7d8c529ba3c75ba405481d50f35a420609b0db827e2e3bb5"}, + {file = "regex-2021.7.1-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:1d386402ae7f3c9b107ae5863f7ecccb0167762c82a687ae6526b040feaa5ac6"}, + {file = "regex-2021.7.1-cp37-cp37m-win32.whl", hash = "sha256:5049d00dbb78f9d166d1c704e93934d42cce0570842bb1a61695123d6b01de09"}, + {file = "regex-2021.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:361be4d311ac995a8c7ad577025a3ae3a538531b1f2cf32efd8b7e5d33a13e5a"}, + {file = "regex-2021.7.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f32f47fb22c988c0b35756024b61d156e5c4011cb8004aa53d93b03323c45657"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:b024ee43ee6b310fad5acaee23e6485b21468718cb792a9d1693eecacc3f0b7e"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:b092754c06852e8a8b022004aff56c24b06310189186805800d09313c37ce1f8"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:a8a5826d8a1b64e2ff9af488cc179e1a4d0f144d11ce486a9f34ea38ccedf4ef"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:444723ebaeb7fa8125f29c01a31101a3854ac3de293e317944022ae5effa53a4"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:fdad3122b69cdabdb3da4c2a4107875913ac78dab0117fc73f988ad589c66b66"}, + {file = "regex-2021.7.1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:4b1999ef60c45357598935c12508abf56edbbb9c380df6f336de38a6c3a294ae"}, + {file = "regex-2021.7.1-cp38-cp38-win32.whl", hash = "sha256:e07e92935040c67f49571779d115ecb3e727016d42fb36ee0d8757db4ca12ee0"}, + {file = "regex-2021.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:6b8b629f93246e507287ee07e26744beaffb4c56ed520576deac8b615bd76012"}, + {file = "regex-2021.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:56bef6b414949e2c9acf96cb5d78de8b529c7b99752619494e78dc76f99fd005"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:78a2a885345a2d60b5e68099e877757d5ed12e46ba1e87507175f14f80892af3"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:3f7a92e60930f8fca2623d9e326c173b7cf2c8b7e4fdcf984b75a1d2fb08114d"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4fc86b729ab88fe8ac3ec92287df253c64aa71560d76da5acd8a2e245839c629"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:59845101de68fd5d3a1145df9ea022e85ecd1b49300ea68307ad4302320f6f61"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:ce269e903b00d1ab4746793e9c50a57eec5d5388681abef074d7b9a65748fca5"}, + {file = "regex-2021.7.1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:c11f2fca544b5e30a0e813023196a63b1cb9869106ef9a26e9dae28bce3e4e26"}, + {file = "regex-2021.7.1-cp39-cp39-win32.whl", hash = "sha256:1ccbd41dbee3a31e18938096510b7d4ee53aa9fce2ee3dcc8ec82ae264f6acfd"}, + {file = "regex-2021.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:18040755606b0c21281493ec309214bd61e41a170509e5014f41d6a5a586e161"}, + {file = "regex-2021.7.1.tar.gz", hash = "sha256:849802379a660206277675aa5a5c327f5c910c690649535863ddf329b0ba8c87"}, ] requests = [ - {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"}, - {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"}, + {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"}, + {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"}, ] rfc3986 = [ {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"}, @@ -1041,20 +1155,20 @@ shellingham = [ {file = "shellingham-1.4.0.tar.gz", hash = "sha256:4855c2458d6904829bd34c299f11fdeed7cfefbf8a2c522e4caea6cd76b3171e"}, ] six = [ - {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"}, - {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"}, + {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, + {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] sniffio = [ - {file = "sniffio-1.1.0-py3-none-any.whl", hash = "sha256:20ed6d5b46f8ae136d00b9dcb807615d83ed82ceea6b2058cecb696765246da5"}, - {file = "sniffio-1.1.0.tar.gz", hash = "sha256:8e3810100f69fe0edd463d02ad407112542a11ffdc29f67db2bf3771afb87a21"}, + {file = "sniffio-1.2.0-py3-none-any.whl", hash = "sha256:471b71698eac1c2112a40ce2752bb2f4a4814c22a54a3eed3676bc0f5ca9f663"}, + {file = "sniffio-1.2.0.tar.gz", hash = "sha256:c4666eecec1d3f50960c6bdf61ab7bc350648da6c126e3cf6898d8cd4ddcd3de"}, ] taskipy = [ - {file = "taskipy-1.7.0-py3-none-any.whl", hash = "sha256:9e284c10898e9dee01a3e72220b94b192b1daa0f560271503a6df1da53d03844"}, - {file = "taskipy-1.7.0.tar.gz", hash = "sha256:960e480b1004971e76454ecd1a0484e640744a30073a1069894a311467f85ed8"}, + {file = "taskipy-1.8.1-py3-none-any.whl", hash = "sha256:2b98f499966e40175d1f1306a64587f49dfa41b90d0d86c8f28b067cc58d0a56"}, + {file = "taskipy-1.8.1.tar.gz", hash = "sha256:7a2404125817e45d80e13fa663cae35da6e8ba590230094e815633653e25f98f"}, ] toml = [ - {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"}, - {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"}, + {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, + {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] typed-ast = [ {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, @@ -1096,16 +1210,32 @@ typer-cli = [ {file = "typer-cli-0.0.12.tar.gz", hash = "sha256:d2c4a7a5c0326c20fb0970eed3c2173f76ba6b8b33d9bbece3a3dd91d673f096"}, {file = "typer_cli-0.0.12-py3-none-any.whl", hash = "sha256:f9b810d4fbdb750b28ceaa5fd8f737db596570418ae092e6d54a64d378e843ca"}, ] +types-certifi = [ + {file = "types-certifi-0.1.4.tar.gz", hash = "sha256:7c134d978f15e4aa2d2b1a85b2a92241ed6b256c3452511b7783b6a28b304b71"}, + {file = "types_certifi-0.1.4-py2.py3-none-any.whl", hash = "sha256:afe4d94726491d843f10e5746797689ea5dcbd78454a653be47d72a8c8ce3bed"}, +] +types-dataclasses = [ + {file = "types-dataclasses-0.1.5.tar.gz", hash = "sha256:7b5f4099fb21c209f2df3a83c2b64308c29955769d610a457244dc0eebe1cafc"}, + {file = "types_dataclasses-0.1.5-py2.py3-none-any.whl", hash = "sha256:c19491cfb981bff9cafd9c113c291a7a54adccc6298ded8ca3de0d7abe211984"}, +] +types-python-dateutil = [ + {file = "types-python-dateutil-0.1.4.tar.gz", hash = "sha256:e6486ca27b6dde73e0ec079a9e1b03e208766e6bc7f1e08964a7e9104a5c7d7a"}, + {file = "types_python_dateutil-0.1.4-py2.py3-none-any.whl", hash = "sha256:39bfe0bde61fc673b8fa28167bd78622d976210f791971b9f3e10877cbf119a4"}, +] +types-pyyaml = [ + {file = "types-PyYAML-5.4.3.tar.gz", hash = "sha256:2e7b81b2b7af751634425107b986086c6ba7cb61270a43a5c290c58be8cdbc3a"}, + {file = "types_PyYAML-5.4.3-py2.py3-none-any.whl", hash = "sha256:bca83cbfc0be48600a8abf1e3d87fb762a91e6d35d724029a3321dd2dce2ceb1"}, +] typing-extensions = [ - {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"}, - {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"}, - {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"}, + {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, + {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, + {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, ] urllib3 = [ - {file = "urllib3-1.25.10-py2.py3-none-any.whl", hash = "sha256:e7983572181f5e1522d9c98453462384ee92a0be7fac5f1413a1e35c56cc0461"}, - {file = "urllib3-1.25.10.tar.gz", hash = "sha256:91056c15fa70756691db97756772bb1eb9678fa585d9184f24534b100dc60f4a"}, + {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, + {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, ] zipp = [ - {file = "zipp-3.3.0-py3-none-any.whl", hash = "sha256:eed8ec0b8d1416b2ca33516a37a08892442f3954dee131e92cfd92d8fe3e7066"}, - {file = "zipp-3.3.0.tar.gz", hash = "sha256:64ad89efee774d1897a58607895d80789c59778ea02185dd846ac38394a8642b"}, + {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, + {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, ] diff --git a/pyproject.toml b/pyproject.toml index 56f02de43..9cebb85b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -47,6 +47,10 @@ pytest-cov = "*" python-multipart = "*" flake8 = "*" typer-cli = "^0.0.12" +types-PyYAML = "^5.4.3" +types-certifi = "^0.1.4" +types-python-dateutil = "^0.1.4" +types-dataclasses = { version = "^0.1.5", python = "<3.7" } [tool.taskipy.tasks] check = """ diff --git a/tests/conftest.py b/tests/conftest.py index 967640ff2..bd1195f98 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,19 @@ -from typing import Callable +from typing import Any, Callable, Dict import pytest -from openapi_python_client.parser.properties import EnumProperty, ModelProperty +from openapi_python_client.parser.properties import ( + AnyProperty, + DateProperty, + DateTimeProperty, + EnumProperty, + FileProperty, + ListProperty, + ModelProperty, + Property, + StringProperty, + UnionProperty, +) @pytest.fixture @@ -15,17 +26,15 @@ def model_property_factory() -> Callable[..., ModelProperty]: from openapi_python_client.parser.properties import Class def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) kwargs = { - "name": "", "description": "", - "required": True, - "nullable": True, - "default": None, - "class_info": Class(name="", module_name=""), + "class_info": Class(name="MyClass", module_name="my_module"), "required_properties": [], "optional_properties": [], "relative_imports": set(), "additional_properties": False, + "python_name": "", **kwargs, } return ModelProperty(**kwargs) @@ -43,11 +52,8 @@ def enum_property_factory() -> Callable[..., EnumProperty]: from openapi_python_client.parser.properties import Class def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) kwargs = { - "name": "test", - "required": True, - "nullable": False, - "default": None, "class_info": Class(name="", module_name=""), "values": {}, "value_type": str, @@ -56,3 +62,140 @@ def _factory(**kwargs): return EnumProperty(**kwargs) return _factory + + +@pytest.fixture +def property_factory() -> Callable[..., Property]: + """ + This fixture surfaces in the test as a function which manufactures Properties with defaults. + + You can pass the same params into this as the Property constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return Property(**kwargs) + + return _factory + + +@pytest.fixture +def any_property_factory() -> Callable[..., AnyProperty]: + """ + This fixture surfaces in the test as a function which manufactures AnyProperty with defaults. + + You can pass the same params into this as the AnyProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return AnyProperty(**kwargs) + + return _factory + + +@pytest.fixture +def string_property_factory() -> Callable[..., StringProperty]: + """ + This fixture surfaces in the test as a function which manufactures StringProperties with defaults. + + You can pass the same params into this as the StringProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return StringProperty(**kwargs) + + return _factory + + +@pytest.fixture +def date_time_property_factory() -> Callable[..., DateTimeProperty]: + """ + This fixture surfaces in the test as a function which manufactures DateTimeProperties with defaults. + + You can pass the same params into this as the DateTimeProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return DateTimeProperty(**kwargs) + + return _factory + + +@pytest.fixture +def date_property_factory() -> Callable[..., DateProperty]: + """ + This fixture surfaces in the test as a function which manufactures DateProperties with defaults. + + You can pass the same params into this as the DateProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return DateProperty(**kwargs) + + return _factory + + +@pytest.fixture +def file_property_factory() -> Callable[..., FileProperty]: + """ + This fixture surfaces in the test as a function which manufactures FileProperties with defaults. + + You can pass the same params into this as the FileProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + return FileProperty(**kwargs) + + return _factory + + +@pytest.fixture +def list_property_factory(string_property_factory) -> Callable[..., ListProperty]: + """ + This fixture surfaces in the test as a function which manufactures ListProperties with defaults. + + You can pass the same params into this as the ListProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + if "inner_property" not in kwargs: + kwargs["inner_property"] = string_property_factory() + return ListProperty(**kwargs) + + return _factory + + +@pytest.fixture +def union_property_factory(date_time_property_factory, string_property_factory) -> Callable[..., UnionProperty]: + """ + This fixture surfaces in the test as a function which manufactures UnionProperties with defaults. + + You can pass the same params into this as the UnionProperty constructor to override defaults. + """ + + def _factory(**kwargs): + kwargs = _common_kwargs(kwargs) + if "inner_properties" not in kwargs: + kwargs["inner_properties"] = [date_time_property_factory(), string_property_factory()] + return UnionProperty(**kwargs) + + return _factory + + +def _common_kwargs(kwargs: Dict[str, Any]) -> Dict[str, Any]: + kwargs = { + "name": "test", + "required": True, + "nullable": False, + "default": None, + **kwargs, + } + if not kwargs.get("python_name"): + kwargs["python_name"] = kwargs["name"] + return kwargs diff --git a/tests/test_config.py b/tests/test_config.py index d0cdbcd2b..eb2ba09ee 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -22,11 +22,9 @@ def test_load_from_path(mocker): config = Config.load_from_path(fake_path) safe_load.assert_called() - assert utils.FIELD_PREFIX == "blah" + assert config.field_prefix == "blah" assert config.class_overrides["Class1"] == override1 assert config.class_overrides["Class2"] == override2 assert config.project_name_override == "project-name" assert config.package_name_override == "package_name" assert config.package_version_override == "package_version" - - utils.FIELD_PREFIX = "field_" diff --git a/tests/test_parser/test_openapi.py b/tests/test_parser/test_openapi.py index df234b8aa..ec2ee1fde 100644 --- a/tests/test_parser/test_openapi.py +++ b/tests/test_parser/test_openapi.py @@ -460,12 +460,12 @@ def test__add_responses(self, mocker): response_1 = Response( status_code=200, source="source", - prop=DateTimeProperty(name="datetime", required=True, nullable=False, default=None), + prop=DateTimeProperty(name="datetime", required=True, nullable=False, default=None, python_name="datetime"), ) response_2 = Response( status_code=404, source="source", - prop=DateProperty(name="date", required=True, nullable=False, default=None), + prop=DateProperty(name="date", required=True, nullable=False, default=None, python_name="date"), ) response_from_data = mocker.patch( f"{MODULE_NAME}.response_from_data", side_effect=[(response_1, schemas_1), (response_2, schemas_2)] @@ -1004,7 +1004,7 @@ def test_from_data_tags_snake_case_sanitizer(self, mocker): path_1_put = oai.Operation.construct() path_1_post = oai.Operation.construct(tags=["AMF Subscription Info (Document)", "tag_3"]) - path_2_get = oai.Operation.construct() + path_2_get = oai.Operation.construct(tags=["3. ABC"]) data = { "path_1": oai.PathItem.construct(post=path_1_post, put=path_1_put), "path_2": oai.PathItem.construct(get=path_2_get), @@ -1039,16 +1039,17 @@ def test_from_data_tags_snake_case_sanitizer(self, mocker): config=config, ), mocker.call( - data=path_2_get, path="path_2", method="get", tag="default", schemas=schemas_2, config=config + data=path_2_get, path="path_2", method="get", tag="tag3_abc", schemas=schemas_2, config=config ), ], ) assert result == ( { - "default": EndpointCollection("default", endpoints=[endpoint_1, endpoint_3]), + "default": EndpointCollection("default", endpoints=[endpoint_1]), "amf_subscription_info_document": EndpointCollection( "amf_subscription_info_document", endpoints=[endpoint_2] ), + "tag3_abc": EndpointCollection("tag3_abc", endpoints=[endpoint_3]), }, schemas_3, ) diff --git a/tests/test_parser/test_properties/test_init.py b/tests/test_parser/test_properties/test_init.py index 574dcf7e9..6d0c53a85 100644 --- a/tests/test_parser/test_properties/test_init.py +++ b/tests/test_parser/test_properties/test_init.py @@ -6,399 +6,212 @@ import openapi_python_client.schema as oai from openapi_python_client import Config from openapi_python_client.parser.errors import PropertyError, ValidationError -from openapi_python_client.parser.properties import BooleanProperty, FloatProperty, IntProperty +from openapi_python_client.parser.properties import BooleanProperty, FloatProperty, IntProperty, Schemas MODULE_NAME = "openapi_python_client.parser.properties" -class TestProperty: - @pytest.mark.parametrize( - "nullable,required,no_optional,json,expected", - [ - (False, False, False, False, "Union[Unset, TestType]"), - (False, False, True, False, "TestType"), - (False, True, False, False, "TestType"), - (False, True, True, False, "TestType"), - (True, False, False, False, "Union[Unset, None, TestType]"), - (True, False, True, False, "TestType"), - (True, True, False, False, "Optional[TestType]"), - (True, True, True, False, "TestType"), - (False, False, False, True, "Union[Unset, str]"), - (False, False, True, True, "str"), - (False, True, False, True, "str"), - (False, True, True, True, "str"), - (True, False, False, True, "Union[Unset, None, str]"), - (True, False, True, True, "str"), - (True, True, False, True, "Optional[str]"), - (True, True, True, True, "str"), - ], - ) - def test_get_type_string(self, mocker, nullable, required, no_optional, json, expected): - from openapi_python_client.parser.properties import Property - - mocker.patch.object(Property, "_type_string", "TestType") - mocker.patch.object(Property, "_json_type_string", "str") - p = Property(name="test", required=required, default=None, nullable=nullable) - assert p.get_type_string(no_optional=no_optional, json=json) == expected - +class TestStringProperty: @pytest.mark.parametrize( - "default,required,expected", - [ - (None, False, "test: Union[Unset, TestType] = UNSET"), - (None, True, "test: TestType"), - ("Test", False, "test: Union[Unset, TestType] = Test"), - ("Test", True, "test: TestType = Test"), - ], + "required, nullable, expected", + ( + (True, False, "str"), + (True, True, "Optional[str]"), + (False, True, "Union[Unset, None, str]"), + (False, False, "Union[Unset, str]"), + ), ) - def test_to_string(self, mocker, default, required, expected): - from openapi_python_client.parser.properties import Property - - name = "test" - mocker.patch.object(Property, "_type_string", "TestType") - p = Property(name=name, required=required, default=default, nullable=False) - assert p.to_string() == expected - - def test_get_imports(self): - from openapi_python_client.parser.properties import Property - - p = Property(name="test", required=True, default=None, nullable=False) - assert p.get_imports(prefix="") == set() - - p = Property(name="test", required=False, default=None, nullable=False) - assert p.get_imports(prefix="") == {"from types import UNSET, Unset", "from typing import Union"} - - p = Property(name="test", required=False, default=None, nullable=True) - assert p.get_imports(prefix="") == { - "from types import UNSET, Unset", - "from typing import Optional", - "from typing import Union", - } - - -class TestStringProperty: - def test_get_type_string(self): - from openapi_python_client.parser.properties import StringProperty - - p = StringProperty(name="test", required=True, default=None, nullable=False) + def test_get_type_string(self, string_property_factory, required, nullable, expected): + p = string_property_factory(required=required, nullable=nullable) - base_type_string = f"str" - - assert p.get_type_string() == base_type_string - assert p.get_type_string(json=True) == base_type_string - - p = StringProperty(name="test", required=True, default=None, nullable=True) - assert p.get_type_string() == f"Optional[{base_type_string}]" - - p = StringProperty(name="test", required=False, default=None, nullable=True) - assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" - - p = StringProperty(name="test", required=False, default=None, nullable=False) - assert p.get_type_string() == f"Union[Unset, {base_type_string}]" + assert p.get_type_string() == expected class TestDateTimeProperty: - def test_get_imports(self): - from openapi_python_client.parser.properties import DateTimeProperty + @pytest.mark.parametrize("required", (True, False)) + @pytest.mark.parametrize("nullable", (True, False)) + def test_get_imports(self, date_time_property_factory, required, nullable): + p = date_time_property_factory(required=required, nullable=nullable) - p = DateTimeProperty(name="test", required=True, default=None, nullable=False) - assert p.get_imports(prefix="...") == { + expected = { "import datetime", "from typing import cast", "from dateutil.parser import isoparse", } + if nullable: + expected.add("from typing import Optional") + if not required: + expected |= { + "from typing import Union", + "from ...types import UNSET, Unset", + } - p = DateTimeProperty(name="test", required=False, default=None, nullable=False) - assert p.get_imports(prefix="...") == { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import Union", - "from ...types import UNSET, Unset", - } - - p = DateTimeProperty(name="test", required=False, default=None, nullable=True) - assert p.get_imports(prefix="...") == { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import Union", - "from typing import Optional", - "from ...types import UNSET, Unset", - } + assert p.get_imports(prefix="...") == expected class TestDateProperty: - def test_get_imports(self): - from openapi_python_client.parser.properties import DateProperty + @pytest.mark.parametrize("required", (True, False)) + @pytest.mark.parametrize("nullable", (True, False)) + def test_get_imports(self, date_property_factory, required, nullable): + p = date_property_factory(required=required, nullable=nullable) - p = DateProperty(name="test", required=True, default=None, nullable=False) - assert p.get_imports(prefix="...") == { + expected = { "import datetime", "from typing import cast", "from dateutil.parser import isoparse", } + if nullable: + expected.add("from typing import Optional") + if not required: + expected |= { + "from typing import Union", + "from ...types import UNSET, Unset", + } - p = DateProperty(name="test", required=False, default=None, nullable=False) - assert p.get_imports(prefix="...") == { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import Union", - "from ...types import UNSET, Unset", - } - - p = DateProperty(name="test", required=False, default=None, nullable=True) - assert p.get_imports(prefix="...") == { - "import datetime", - "from typing import cast", - "from dateutil.parser import isoparse", - "from typing import Union", - "from typing import Optional", - "from ...types import UNSET, Unset", - } + assert p.get_imports(prefix="...") == expected class TestFileProperty: - def test_get_imports(self): - from openapi_python_client.parser.properties import FileProperty - - prefix = "..." - p = FileProperty(name="test", required=True, default=None, nullable=False) - assert p.get_imports(prefix=prefix) == { - "from io import BytesIO", - "from ...types import File, FileJsonType", - } + @pytest.mark.parametrize("required", (True, False)) + @pytest.mark.parametrize("nullable", (True, False)) + def test_get_imports(self, file_property_factory, required, nullable): + p = file_property_factory(required=required, nullable=nullable) - p = FileProperty(name="test", required=False, default=None, nullable=False) - assert p.get_imports(prefix=prefix) == { + expected = { "from io import BytesIO", "from ...types import File, FileJsonType", - "from typing import Union", - "from ...types import UNSET, Unset", } + if nullable: + expected.add("from typing import Optional") + if not required: + expected |= { + "from typing import Union", + "from ...types import UNSET, Unset", + } - p = FileProperty(name="test", required=False, default=None, nullable=True) - assert p.get_imports(prefix=prefix) == { - "from io import BytesIO", - "from ...types import File, FileJsonType", - "from typing import Union", - "from typing import Optional", - "from ...types import UNSET, Unset", - } + assert p.get_imports(prefix="...") == expected class TestListProperty: - def test_get_type_string(self, mocker): - from openapi_python_client.parser.properties import ListProperty - - inner_property = mocker.MagicMock() - inner_type_string = mocker.MagicMock() - inner_property.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "int" if json else inner_type_string - ) - p = ListProperty(name="test", required=True, default=None, inner_property=inner_property, nullable=False) - - base_type_string = f"List[{inner_type_string}]" - - assert p.get_type_string() == base_type_string - assert p.get_type_string(json=True) == "List[int]" - - p = ListProperty(name="test", required=True, default=None, inner_property=inner_property, nullable=True) - assert p.get_type_string() == f"Optional[{base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=True) - assert p.get_type_string() == f"Union[Unset, None, {base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=False) - assert p.get_type_string() == f"Union[Unset, {base_type_string}]" - assert p.get_type_string(no_optional=True) == base_type_string - - def test_get_type_imports(self, mocker): - from openapi_python_client.parser.properties import ListProperty + @pytest.mark.parametrize( + "required, nullable, expected", + ( + (True, False, "List[str]"), + (True, True, "Optional[List[str]]"), + (False, False, "Union[Unset, List[str]]"), + (False, True, "Union[Unset, None, List[str]]"), + ), + ) + def test_get_type_string(self, list_property_factory, required, nullable, expected): + p = list_property_factory(required=required, nullable=nullable) - inner_property = mocker.MagicMock() - inner_import = mocker.MagicMock() - inner_property.get_imports.return_value = {inner_import} - prefix = "..." - p = ListProperty(name="test", required=True, default=None, inner_property=inner_property, nullable=False) + assert p.get_type_string() == expected - assert p.get_imports(prefix=prefix) == { - inner_import, + @pytest.mark.parametrize("required", (True, False)) + @pytest.mark.parametrize("nullable", (True, False)) + def test_get_type_imports(self, list_property_factory, date_time_property_factory, required, nullable): + inner_property = date_time_property_factory() + p = list_property_factory(inner_property=inner_property, required=required, nullable=nullable) + expected = { + "import datetime", + "from typing import cast", + "from dateutil.parser import isoparse", "from typing import cast, List", } + if nullable: + expected.add("from typing import Optional") + if not required: + expected |= { + "from typing import Union", + "from ...types import UNSET, Unset", + } - p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=False) - assert p.get_imports(prefix=prefix) == { - inner_import, - "from typing import cast, List", - "from typing import Union", - "from ...types import UNSET, Unset", - } - - p = ListProperty(name="test", required=False, default=None, inner_property=inner_property, nullable=True) - assert p.get_imports(prefix=prefix) == { - inner_import, - "from typing import cast, List", - "from typing import Union", - "from typing import Optional", - "from ...types import UNSET, Unset", - } + assert p.get_imports(prefix="...") == expected class TestUnionProperty: @pytest.mark.parametrize( "nullable,required,no_optional,json,expected", [ - (False, False, False, False, "Union[Unset, inner_type_string_1, inner_type_string_2]"), - (False, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), - (False, True, False, False, "Union[inner_type_string_1, inner_type_string_2]"), - (False, True, True, False, "Union[inner_type_string_1, inner_type_string_2]"), - (True, False, False, False, "Union[None, Unset, inner_type_string_1, inner_type_string_2]"), - (True, False, True, False, "Union[inner_type_string_1, inner_type_string_2]"), - (True, True, False, False, "Union[None, inner_type_string_1, inner_type_string_2]"), - (True, True, True, False, "Union[inner_type_string_1, inner_type_string_2]"), - (False, False, False, True, "Union[Unset, inner_json_type_string_1, inner_json_type_string_2]"), - (False, False, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), - (False, True, False, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), - (False, True, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), - (True, False, False, True, "Union[None, Unset, inner_json_type_string_1, inner_json_type_string_2]"), - (True, False, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), - (True, True, False, True, "Union[None, inner_json_type_string_1, inner_json_type_string_2]"), - (True, True, True, True, "Union[inner_json_type_string_1, inner_json_type_string_2]"), + (False, False, False, False, "Union[Unset, datetime.datetime, str]"), + (False, False, True, False, "Union[datetime.datetime, str]"), + (False, True, False, False, "Union[datetime.datetime, str]"), + (False, True, True, False, "Union[datetime.datetime, str]"), + (True, False, False, False, "Union[None, Unset, datetime.datetime, str]"), + (True, False, True, False, "Union[datetime.datetime, str]"), + (True, True, False, False, "Union[None, datetime.datetime, str]"), + (True, True, True, False, "Union[datetime.datetime, str]"), + (False, False, False, True, "Union[Unset, str]"), + (False, False, True, True, "str"), + (False, True, False, True, "str"), + (False, True, True, True, "str"), + (True, False, False, True, "Union[None, Unset, str]"), + (True, False, True, True, "str"), + (True, True, False, True, "Union[None, str]"), + (True, True, True, True, "str"), ], ) - def test_get_type_string(self, mocker, nullable, required, no_optional, json, expected): - from openapi_python_client.parser.properties import UnionProperty - - inner_property_1 = mocker.MagicMock() - inner_property_1.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" - ) - inner_property_2 = mocker.MagicMock() - inner_property_2.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" - ) - p = UnionProperty( - name="test", + def test_get_type_string( + self, + union_property_factory, + date_time_property_factory, + string_property_factory, + nullable, + required, + no_optional, + json, + expected, + ): + p = union_property_factory( required=required, - default=None, - inner_properties=[inner_property_1, inner_property_2], nullable=nullable, + inner_properties=[date_time_property_factory(), string_property_factory()], ) - assert p.get_type_string(no_optional=no_optional, json=json) == expected - def test_get_base_type_string(self, mocker): - from openapi_python_client.parser.properties import UnionProperty + assert p.get_base_type_string() == "Union[datetime.datetime, str]" - inner_property_1 = mocker.MagicMock() - inner_property_1.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" - ) - inner_property_2 = mocker.MagicMock() - inner_property_2.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" - ) - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=True, - ) - assert p.get_base_type_string() == "Union[inner_type_string_1, inner_type_string_2]" + assert p.get_type_string(no_optional=no_optional, json=json) == expected - def test_get_base_type_string_one_element(self, mocker): - from openapi_python_client.parser.properties import UnionProperty + def test_get_base_type_string(self, union_property_factory, date_time_property_factory, string_property_factory): + p = union_property_factory(inner_properties=[date_time_property_factory(), string_property_factory()]) - inner_property_1 = mocker.MagicMock() - inner_property_1.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" - ) - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1], - nullable=True, - ) - assert p.get_base_type_string() == "inner_type_string_1" + assert p.get_base_type_string() == "Union[datetime.datetime, str]" - def test_get_base_json_type_string(self, mocker): - from openapi_python_client.parser.properties import UnionProperty - - inner_property_1 = mocker.MagicMock() - inner_property_1.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_1" if json else "inner_type_string_1" - ) - inner_property_2 = mocker.MagicMock() - inner_property_2.get_type_string.side_effect = ( - lambda no_optional=False, json=False: "inner_json_type_string_2" if json else "inner_type_string_2" - ) - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=True, + def test_get_base_type_string_one_element(self, union_property_factory, date_time_property_factory): + p = union_property_factory( + inner_properties=[date_time_property_factory()], ) - assert p.get_base_json_type_string() == "Union[inner_json_type_string_1, inner_json_type_string_2]" - def test_get_imports(self, mocker): - from openapi_python_client.parser.properties import UnionProperty + assert p.get_base_type_string() == "datetime.datetime" - inner_property_1 = mocker.MagicMock() - inner_import_1 = mocker.MagicMock() - inner_property_1.get_imports.return_value = {inner_import_1} - inner_property_2 = mocker.MagicMock() - inner_import_2 = mocker.MagicMock() - inner_property_2.get_imports.return_value = {inner_import_2} - prefix = "..." - p = UnionProperty( - name="test", - required=True, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=False, + def test_get_base_json_type_string(self, union_property_factory, date_time_property_factory): + p = union_property_factory( + inner_properties=[date_time_property_factory()], ) - assert p.get_imports(prefix=prefix) == { - inner_import_1, - inner_import_2, - "from typing import cast, Union", - } + assert p.get_base_json_type_string() == "str" - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=False, - ) - assert p.get_imports(prefix=prefix) == { - inner_import_1, - inner_import_2, - "from typing import Union", + @pytest.mark.parametrize("required", (True, False)) + @pytest.mark.parametrize("nullable", (True, False)) + def test_get_type_imports(self, union_property_factory, date_time_property_factory, required, nullable): + p = union_property_factory( + inner_properties=[date_time_property_factory()], required=required, nullable=nullable + ) + expected = { + "import datetime", + "from typing import cast", + "from dateutil.parser import isoparse", "from typing import cast, Union", - "from ...types import UNSET, Unset", } + if nullable: + expected.add("from typing import Optional") + if not required: + expected |= { + "from typing import Union", + "from ...types import UNSET, Unset", + } - p = UnionProperty( - name="test", - required=False, - default=None, - inner_properties=[inner_property_1, inner_property_2], - nullable=True, - ) - assert p.get_imports(prefix=prefix) == { - inner_import_1, - inner_import_2, - "from typing import Union", - "from typing import cast, Union", - "from typing import Optional", - "from ...types import UNSET, Unset", - } + assert p.get_imports(prefix="...") == expected class TestEnumProperty: @@ -462,26 +275,24 @@ def test_values_from_list_duplicate(self): class TestPropertyFromData: - def test_property_from_data_str_enum(self, mocker): - from openapi_python_client.parser.properties import Class, EnumProperty + def test_property_from_data_str_enum(self, enum_property_factory): + from openapi_python_client.parser.properties import Class, Schemas, property_from_data from openapi_python_client.schema import Schema + existing = enum_property_factory() data = Schema(title="AnEnum", enum=["A", "B", "C"], nullable=False, default="B") name = "my_enum" required = True - from openapi_python_client.parser.properties import Schemas, property_from_data - - schemas = Schemas(classes_by_name={"AnEnum": mocker.MagicMock()}) + schemas = Schemas(classes_by_name={"AnEnum": existing}) prop, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) - assert prop == EnumProperty( - name="my_enum", - required=True, - nullable=False, + assert prop == enum_property_factory( + name=name, + required=required, values={"A": "A", "B": "B", "C": "C"}, class_info=Class(name="ParentAnEnum", module_name="parent_an_enum"), value_type=str, @@ -489,30 +300,30 @@ def test_property_from_data_str_enum(self, mocker): ) assert schemas != new_schemas, "Provided Schemas was mutated" assert new_schemas.classes_by_name == { - "AnEnum": schemas.classes_by_name["AnEnum"], + "AnEnum": existing, "ParentAnEnum": prop, } - def test_property_from_data_int_enum(self, mocker): - from openapi_python_client.parser.properties import Class, EnumProperty + def test_property_from_data_int_enum(self, enum_property_factory): + from openapi_python_client.parser.properties import Class, EnumProperty, Schemas, property_from_data from openapi_python_client.schema import Schema - data = Schema.construct(title="anEnum", enum=[1, 2, 3], nullable=False, default=3) name = "my_enum" required = True + nullable = False + data = Schema.construct(title="anEnum", enum=[1, 2, 3], nullable=nullable, default=3) - from openapi_python_client.parser.properties import Schemas, property_from_data - - schemas = Schemas(classes_by_name={"AnEnum": mocker.MagicMock()}) + existing = enum_property_factory() + schemas = Schemas(classes_by_name={"AnEnum": existing}) prop, new_schemas = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) - assert prop == EnumProperty( - name="my_enum", - required=True, - nullable=False, + assert prop == enum_property_factory( + name=name, + required=required, + nullable=nullable, values={"VALUE_1": 1, "VALUE_2": 2, "VALUE_3": 3}, class_info=Class(name="ParentAnEnum", module_name="parent_an_enum"), value_type=int, @@ -520,22 +331,19 @@ def test_property_from_data_int_enum(self, mocker): ) assert schemas != new_schemas, "Provided Schemas was mutated" assert new_schemas.classes_by_name == { - "AnEnum": schemas.classes_by_name["AnEnum"], + "AnEnum": existing, "ParentAnEnum": prop, } - def test_property_from_data_ref_enum(self): - from openapi_python_client.parser.properties import Class, EnumProperty, Schemas, property_from_data + def test_property_from_data_ref_enum(self, enum_property_factory): + from openapi_python_client.parser.properties import Class, Schemas, property_from_data name = "some_enum" data = oai.Reference.construct(ref="#/components/schemas/MyEnum") - existing_enum = EnumProperty( + existing_enum = enum_property_factory( name="an_enum", - required=True, - nullable=False, - default=None, + required=False, values={"A": "a"}, - value_type=str, class_info=Class(name="MyEnum", module_name="my_enum"), ) schemas = Schemas(classes_by_reference={"/components/schemas/MyEnum": existing_enum}) @@ -544,61 +352,53 @@ def test_property_from_data_ref_enum(self): name=name, required=False, data=data, schemas=schemas, parent_name="", config=Config() ) - assert prop == EnumProperty( + assert prop == enum_property_factory( name="some_enum", required=False, - nullable=False, - default=None, values={"A": "a"}, - value_type=str, class_info=Class(name="MyEnum", module_name="my_enum"), ) assert schemas == new_schemas - def test_property_from_data_ref_enum_with_overridden_default(self): - from openapi_python_client.parser.properties import Class, EnumProperty, Schemas, property_from_data + def test_property_from_data_ref_enum_with_overridden_default(self, enum_property_factory): + from openapi_python_client.parser.properties import Class, Schemas, property_from_data name = "some_enum" + required = False data = oai.Schema.construct(default="b", allOf=[oai.Reference.construct(ref="#/components/schemas/MyEnum")]) - existing_enum = EnumProperty( + existing_enum = enum_property_factory( name="an_enum", - required=True, - nullable=False, default="MyEnum.A", + required=required, values={"A": "a", "B": "b"}, - value_type=str, class_info=Class(name="MyEnum", module_name="my_enum"), ) schemas = Schemas(classes_by_reference={"/components/schemas/MyEnum": existing_enum}) prop, new_schemas = property_from_data( - name=name, required=False, data=data, schemas=schemas, parent_name="", config=Config() + name=name, required=required, data=data, schemas=schemas, parent_name="", config=Config() ) - assert prop == EnumProperty( + assert prop == enum_property_factory( name="some_enum", - required=False, - nullable=False, default="MyEnum.B", + required=required, values={"A": "a", "B": "b"}, - value_type=str, class_info=Class(name="MyEnum", module_name="my_enum"), ) assert schemas == new_schemas - def test_property_from_data_ref_enum_with_invalid_default(self): - from openapi_python_client.parser.properties import Class, EnumProperty, Schemas, property_from_data + def test_property_from_data_ref_enum_with_invalid_default(self, enum_property_factory): + from openapi_python_client.parser.properties import Class, Schemas, property_from_data name = "some_enum" data = oai.Schema.construct(default="x", allOf=[oai.Reference.construct(ref="#/components/schemas/MyEnum")]) - existing_enum = EnumProperty( + existing_enum = enum_property_factory( name="an_enum", - required=True, - nullable=False, default="MyEnum.A", values={"A": "a", "B": "b"}, - value_type=str, class_info=Class(name="MyEnum", module_name="my_enum"), + python_name="an_enum", ) schemas = Schemas(classes_by_reference={"/components/schemas/MyEnum": existing_enum}) @@ -609,24 +409,18 @@ def test_property_from_data_ref_enum_with_invalid_default(self): assert schemas == new_schemas assert prop == PropertyError(data=data, detail="x is an invalid default for enum MyEnum") - def test_property_from_data_ref_model(self): + def test_property_from_data_ref_model(self, model_property_factory): from openapi_python_client.parser.properties import Class, ModelProperty, Schemas, property_from_data name = "new_name" required = False class_name = "MyModel" data = oai.Reference.construct(ref=f"#/components/schemas/{class_name}") - existing_model = ModelProperty( + class_info = Class(name=class_name, module_name="my_model") + + existing_model = model_property_factory( name="old_name", - required=True, - nullable=False, - default=None, - class_info=Class(name=class_name, module_name="my_model"), - required_properties=[], - optional_properties=[], - description="", - relative_imports=set(), - additional_properties=False, + class_info=class_info, ) schemas = Schemas(classes_by_reference={f"/components/schemas/{class_name}": existing_model}) @@ -634,32 +428,22 @@ def test_property_from_data_ref_model(self): name=name, required=required, data=data, schemas=schemas, parent_name="", config=Config() ) - assert prop == ModelProperty( + assert prop == model_property_factory( name=name, required=required, - nullable=False, - default=None, - class_info=Class(name=class_name, module_name="my_model"), - required_properties=[], - optional_properties=[], - description="", - relative_imports=set(), - additional_properties=False, + class_info=class_info, ) assert schemas == new_schemas def test_property_from_data_ref_not_found(self, mocker): from openapi_python_client.parser.properties import PropertyError, Schemas, property_from_data - name = mocker.MagicMock() - required = mocker.MagicMock() - data = oai.Reference.construct(ref=mocker.MagicMock()) + data = oai.Reference.construct(ref="a/b/c") parse_reference_path = mocker.patch(f"{MODULE_NAME}.parse_reference_path") - mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name) schemas = Schemas() prop, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=mocker.MagicMock() + name="a_prop", required=False, data=data, schemas=schemas, parent_name="parent", config=mocker.MagicMock() ) parse_reference_path.assert_called_once_with(data.ref) @@ -685,24 +469,6 @@ def test_property_from_data_invalid_ref(self, mocker): assert prop == PropertyError(data=data, detail="bad stuff") assert schemas == new_schemas - def test_property_from_data_string(self, mocker): - from openapi_python_client.parser.properties import Schemas, property_from_data - - _string_based_property = mocker.patch(f"{MODULE_NAME}._string_based_property") - name = mocker.MagicMock() - required = mocker.MagicMock() - data = oai.Schema.construct(type="string") - mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name) - schemas = Schemas() - - p, new_schemas = property_from_data( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=mocker.MagicMock() - ) - - assert p == _string_based_property.return_value - assert schemas == new_schemas - _string_based_property.assert_called_once_with(name=name, required=required, data=data) - @pytest.mark.parametrize( "openapi_type,prop_type,python_type", [ @@ -723,7 +489,9 @@ def test_property_from_data_simple_types(self, openapi_type, prop_type, python_t name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) - assert p == prop_type(name=name, required=required, default=python_type(data.default), nullable=False) + assert p == prop_type( + name=name, required=required, default=python_type(data.default), nullable=False, python_name=name + ) assert new_schemas == schemas # Test nullable values @@ -733,7 +501,9 @@ def test_property_from_data_simple_types(self, openapi_type, prop_type, python_t p, _ = property_from_data( name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) - assert p == prop_type(name=name, required=required, default=python_type(data.default), nullable=True) + assert p == prop_type( + name=name, required=required, default=python_type(data.default), nullable=True, python_name=name + ) # Test bad default value data.default = "a" @@ -818,12 +588,13 @@ def test_property_from_data_union_of_one_element(self, mocker, model_property_fa name = "new_name" required = False class_name = "MyModel" + nullable = True existing_model = model_property_factory() schemas = Schemas(classes_by_reference={f"/{class_name}": existing_model}) data = oai.Schema.construct( allOf=[oai.Reference.construct(ref=f"#/{class_name}")], - nullable=True, + nullable=nullable, ) build_union_property = mocker.patch(f"{MODULE_NAME}.build_union_property") @@ -831,7 +602,7 @@ def test_property_from_data_union_of_one_element(self, mocker, model_property_fa name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=Config() ) - assert prop == attr.evolve(existing_model, name=name, required=required) + assert prop == attr.evolve(existing_model, name=name, required=required, nullable=nullable, python_name=name) build_union_property.assert_not_called() def test_property_from_data_unsupported_type(self, mocker): @@ -854,12 +625,13 @@ def test_property_from_data_no_valid_props_in_data(self): schemas = Schemas() data = oai.Schema() + name = "blah" prop, new_schemas = property_from_data( - name="blah", required=True, data=data, schemas=schemas, parent_name="parent", config=MagicMock() + name=name, required=True, data=data, schemas=schemas, parent_name="parent", config=MagicMock() ) - assert prop == AnyProperty(name="blah", required=True, nullable=False, default=None) + assert prop == AnyProperty(name=name, required=True, nullable=False, default=None, python_name=name) assert new_schemas == schemas def test_property_from_data_validation_error(self, mocker): @@ -922,68 +694,54 @@ def test_build_list_property_invalid_items(self, mocker): name=f"{name}_item", required=True, data=data.items, schemas=schemas, parent_name="parent", config=config ) - def test_build_list_property(self, mocker): + def test_build_list_property(self, any_property_factory): from openapi_python_client.parser import properties name = "prop" - required = mocker.MagicMock() data = oai.Schema( type="array", items={}, ) - schemas = properties.Schemas() - second_schemas = properties.Schemas(errors=["error"]) - property_from_data = mocker.patch.object( - properties, "property_from_data", return_value=(mocker.MagicMock(), second_schemas) - ) - mocker.patch("openapi_python_client.utils.snake_case", return_value=name) - mocker.patch("openapi_python_client.utils.to_valid_python_identifier", return_value=name) - config = MagicMock() + schemas = properties.Schemas(errors=["error"]) + config = Config() p, new_schemas = properties.build_list_property( - name=name, required=required, data=data, schemas=schemas, parent_name="parent", config=config + name=name, required=True, data=data, schemas=schemas, parent_name="parent", config=config ) assert isinstance(p, properties.ListProperty) - assert p.inner_property == property_from_data.return_value[0] - assert new_schemas == second_schemas - assert schemas != new_schemas, "Schema was mutated" - property_from_data.assert_called_once_with( - name=f"{name}_item", required=True, data=data.items, schemas=schemas, parent_name="parent", config=config - ) + assert p.inner_property == any_property_factory(name=f"{name}_item") + assert new_schemas == schemas class TestBuildUnionProperty: - def test_property_from_data_union(self, mocker): - name = mocker.MagicMock() - required = mocker.MagicMock() + def test_property_from_data_union( + self, union_property_factory, date_time_property_factory, string_property_factory + ): + from openapi_python_client.parser.properties import Schemas, property_from_data + + name = "union_prop" + required = True data = oai.Schema( - anyOf=[{"type": "number", "default": "0.0"}], + anyOf=[{"type": "string", "default": "a"}], oneOf=[ - {"type": "integer", "default": "0"}, + {"type": "string", "format": "date-time"}, + ], + ) + expected = union_property_factory( + name=name, + required=required, + inner_properties=[ + string_property_factory(name=f"{name}_type_0", default="'a'"), + date_time_property_factory(name=f"{name}_type_1"), ], ) - UnionProperty = mocker.patch(f"{MODULE_NAME}.UnionProperty") - FloatProperty = mocker.patch(f"{MODULE_NAME}.FloatProperty") - IntProperty = mocker.patch(f"{MODULE_NAME}.IntProperty") - mocker.patch("openapi_python_client.utils.remove_string_escapes", return_value=name) - - from openapi_python_client.parser.properties import Schemas, property_from_data p, s = property_from_data( name=name, required=required, data=data, schemas=Schemas(), parent_name="parent", config=MagicMock() ) - FloatProperty.assert_called_once_with(name=name, required=required, default=0.0, nullable=False) - IntProperty.assert_called_once_with(name=name, required=required, default=0, nullable=False) - UnionProperty.assert_called_once_with( - name=name, - required=required, - default=None, - inner_properties=[FloatProperty.return_value, IntProperty.return_value], - nullable=False, - ) - assert p == UnionProperty.return_value + assert p == expected assert s == Schemas() def test_property_from_data_union_bad_type(self, mocker): @@ -1002,87 +760,110 @@ def test_property_from_data_union_bad_type(self, mocker): class TestStringBasedProperty: - def test__string_based_property_no_format(self): - from openapi_python_client.parser.properties import StringProperty + @pytest.mark.parametrize("nullable", (True, False)) + @pytest.mark.parametrize("required", (True, False)) + def test_no_format(self, string_property_factory, nullable, required): + from openapi_python_client.parser.properties import property_from_data name = "some_prop" - required = True - data = oai.Schema.construct(type="string", nullable=True, default='"hello world"') + data = oai.Schema.construct(type="string", nullable=nullable, default='"hello world"', pattern="abcdef") - from openapi_python_client.parser.properties import _string_based_property + p, _ = property_from_data( + name=name, required=required, data=data, parent_name=None, config=Config(), schemas=Schemas() + ) - p = _string_based_property(name=name, required=required, data=data) + assert p == string_property_factory( + name=name, required=required, nullable=nullable, default="'\\\\\"hello world\\\\\"'", pattern=data.pattern + ) - assert p == StringProperty(name=name, required=required, nullable=True, default="'\\\\\"hello world\\\\\"'") + def test_datetime_format(self, date_time_property_factory): + from openapi_python_client.parser.properties import property_from_data - data.pattern = "abcdef" - data.nullable = False + name = "datetime_prop" + required = True + data = oai.Schema.construct( + type="string", schema_format="date-time", nullable=True, default="2020-11-06T12:00:00" + ) - p = _string_based_property( - name=name, - required=required, - data=data, + p, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), config=Config(), parent_name=None ) - assert p == StringProperty( - name=name, required=required, nullable=False, default="'\\\\\"hello world\\\\\"'", pattern="abcdef" + + assert p == date_time_property_factory( + name=name, required=required, nullable=True, default=f"isoparse('{data.default}')" ) - def test__string_based_property_datetime_format(self): - from openapi_python_client.parser.properties import DateTimeProperty, _string_based_property + def test_datetime_bad_default(self): + from openapi_python_client.parser.properties import property_from_data name = "datetime_prop" required = True - data = oai.Schema.construct( - type="string", schema_format="date-time", nullable=True, default="2020-11-06T12:00:00" + data = oai.Schema.construct(type="string", schema_format="date-time", nullable=True, default="a") + + result, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), config=Config(), parent_name=None ) - p = _string_based_property(name=name, required=required, data=data) + assert result == PropertyError(detail="Failed to validate default value", data=data) + + def test_date_format(self, date_property_factory): + from openapi_python_client.parser.properties import property_from_data - assert p == DateTimeProperty( - name=name, required=required, nullable=True, default="isoparse('2020-11-06T12:00:00')" + name = "date_prop" + required = True + nullable = True + + data = oai.Schema.construct(type="string", schema_format="date", nullable=nullable, default="2020-11-06") + + p, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), config=Config(), parent_name=None ) - # Test bad default - data.default = "a" - with pytest.raises(ValidationError): - _string_based_property(name=name, required=required, data=data) + assert p == date_property_factory( + name=name, required=required, nullable=nullable, default=f"isoparse('{data.default}').date()" + ) - def test__string_based_property_date_format(self): - from openapi_python_client.parser.properties import DateProperty, _string_based_property + def test_date_format_bad_default(self): + from openapi_python_client.parser.properties import property_from_data name = "date_prop" required = True - data = oai.Schema.construct(type="string", schema_format="date", nullable=True, default="2020-11-06") + nullable = True - p = _string_based_property(name=name, required=required, data=data) + data = oai.Schema.construct(type="string", schema_format="date", nullable=nullable, default="a") - assert p == DateProperty(name=name, required=required, nullable=True, default="isoparse('2020-11-06').date()") + p, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), config=Config(), parent_name=None + ) - # Test bad default - data.default = "a" - with pytest.raises(ValidationError): - _string_based_property(name=name, required=required, data=data) + assert p == PropertyError(detail="Failed to validate default value", data=data) - def test__string_based_property_binary_format(self): - from openapi_python_client.parser.properties import FileProperty, _string_based_property + def test__string_based_property_binary_format(self, file_property_factory): + from openapi_python_client.parser.properties import property_from_data name = "file_prop" required = True - data = oai.Schema.construct(type="string", schema_format="binary", nullable=True, default="a") + nullable = True + data = oai.Schema.construct(type="string", schema_format="binary", nullable=nullable, default="a") - p = _string_based_property(name=name, required=required, data=data) - assert p == FileProperty(name=name, required=required, nullable=True, default=None) + p, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas(), config=Config(), parent_name=None + ) + assert p == file_property_factory(name=name, required=required, nullable=nullable) - def test__string_based_property_unsupported_format(self, mocker): - from openapi_python_client.parser.properties import StringProperty, _string_based_property + def test__string_based_property_unsupported_format(self, string_property_factory): + from openapi_python_client.parser.properties import property_from_data name = "unknown" required = True - data = oai.Schema.construct(type="string", schema_format="blah", nullable=True) + nullable = True + data = oai.Schema.construct(type="string", schema_format="blah", nullable=nullable) - p = _string_based_property(name=name, required=required, data=data) + p, _ = property_from_data( + name=name, required=required, data=data, schemas=Schemas, config=Config(), parent_name=None + ) - assert p == StringProperty(name=name, required=required, nullable=True, default=None) + assert p == string_property_factory(name=name, required=required, nullable=nullable) class TestBuildSchemas: diff --git a/tests/test_parser/test_properties/test_model_property.py b/tests/test_parser/test_properties/test_model_property.py index abe0e5323..da57b3345 100644 --- a/tests/test_parser/test_properties/test_model_property.py +++ b/tests/test_parser/test_properties/test_model_property.py @@ -1,4 +1,3 @@ -from typing import Callable from unittest.mock import MagicMock import pytest @@ -9,12 +8,6 @@ from openapi_python_client.parser.properties import DateTimeProperty, ModelProperty, StringProperty -def get_class(): - from openapi_python_client.parser.properties import Class - - return Class(name="MyClass", module_name="my_module") - - @pytest.mark.parametrize( "no_optional,nullable,required,json,expected", [ @@ -29,40 +22,18 @@ def get_class(): (False, False, True, True, "Dict[str, Any]"), ], ) -def test_get_type_string(no_optional, nullable, required, json, expected): - from openapi_python_client.parser.properties import ModelProperty +def test_get_type_string(no_optional, nullable, required, json, expected, model_property_factory): - prop = ModelProperty( - name="prop", + prop = model_property_factory( required=required, nullable=nullable, - default=None, - class_info=get_class(), - description="", - optional_properties=[], - required_properties=[], - relative_imports=set(), - additional_properties=False, ) assert prop.get_type_string(no_optional=no_optional, json=json) == expected -def test_get_imports(): - from openapi_python_client.parser.properties import ModelProperty - - prop = ModelProperty( - name="prop", - required=False, - nullable=True, - default=None, - class_info=get_class(), - description="", - optional_properties=[], - required_properties=[], - relative_imports=set(), - additional_properties=False, - ) +def test_get_imports(model_property_factory): + prop = model_property_factory(required=False, nullable=True) assert prop.get_imports(prefix="..") == { "from typing import Optional", @@ -84,7 +55,13 @@ class TestBuildModelProperty: (False, False), ( oai.Schema.construct(type="string"), - StringProperty(name="AdditionalProperty", required=True, nullable=False, default=None), + StringProperty( + name="AdditionalProperty", + required=True, + nullable=False, + default=None, + python_name="additional_property", + ), ), ], ) @@ -101,9 +78,13 @@ def test_additional_schemas(self, additional_properties_schema, expected_additio assert model.additional_properties == expected_additional_properties - def test_happy_path(self): + def test_happy_path(self, model_property_factory, string_property_factory, date_time_property_factory): from openapi_python_client.parser.properties import Class, Schemas, build_model_property + name = "prop" + nullable = False + required = True + data = oai.Schema.construct( required=["req"], title="MyModel", @@ -112,12 +93,12 @@ def test_happy_path(self): "opt": oai.Schema(type="string", format="date-time"), }, description="A class called MyModel", - nullable=False, + nullable=nullable, ) schemas = Schemas(classes_by_reference={"OtherModel": None}, classes_by_name={"OtherModel": None}) model, new_schemas = build_model_property( - data=data, name="prop", schemas=schemas, required=True, parent_name="parent", config=Config() + data=data, name=name, schemas=schemas, required=required, parent_name="parent", config=Config() ) assert new_schemas != schemas @@ -128,14 +109,13 @@ def test_happy_path(self): assert new_schemas.classes_by_reference == { "OtherModel": None, } - assert model == ModelProperty( - name="prop", - required=True, - nullable=False, - default=None, + assert model == model_property_factory( + name=name, + required=required, + nullable=nullable, class_info=Class(name="ParentMyModel", module_name="parent_my_model"), - required_properties=[StringProperty(name="req", required=True, nullable=False, default=None)], - optional_properties=[DateTimeProperty(name="opt", required=False, nullable=False, default=None)], + required_properties=[string_property_factory(name="req", required=True)], + optional_properties=[date_time_property_factory(name="opt", required=False)], description=data.description, relative_imports={ "from dateutil.parser import isoparse", @@ -197,19 +177,10 @@ def test_bad_additional_props_return_error(self): assert err == PropertyError(detail="unknown type not_real", data=oai.Schema(type="not_real")) -def string_property(**kwargs) -> StringProperty: - kwargs = { - "name": "", - "required": True, - "nullable": True, - "default": None, - **kwargs, - } - return StringProperty(**kwargs) - - class TestProcessProperties: - def test_conflicting_properties_different_types(self, model_property_factory): + def test_conflicting_properties_different_types( + self, model_property_factory, string_property_factory, date_time_property_factory + ): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties @@ -218,12 +189,8 @@ def test_conflicting_properties_different_types(self, model_property_factory): ) schemas = Schemas( classes_by_reference={ - "/First": model_property_factory( - optional_properties=[StringProperty(name="prop", required=True, nullable=True, default=None)] - ), - "/Second": model_property_factory( - optional_properties=[DateTimeProperty(name="prop", required=True, nullable=True, default=None)] - ), + "/First": model_property_factory(optional_properties=[string_property_factory()]), + "/Second": model_property_factory(optional_properties=[date_time_property_factory()]), } ) @@ -257,7 +224,7 @@ def test_non_model_reference(self, enum_property_factory): assert isinstance(result, PropertyError) - def test_conflicting_properties_same_types(self, model_property_factory): + def test_conflicting_properties_same_types(self, model_property_factory, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties @@ -266,8 +233,8 @@ def test_conflicting_properties_same_types(self, model_property_factory): ) schemas = Schemas( classes_by_reference={ - "/First": model_property_factory(optional_properties=[string_property(default="abc")]), - "/Second": model_property_factory(optional_properties=[string_property()]), + "/First": model_property_factory(optional_properties=[string_property_factory(default="abc")]), + "/Second": model_property_factory(optional_properties=[string_property_factory()]), } ) @@ -275,14 +242,14 @@ def test_conflicting_properties_same_types(self, model_property_factory): assert isinstance(result, PropertyError) - def test_duplicate_properties(self, model_property_factory): + def test_duplicate_properties(self, model_property_factory, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties data = oai.Schema.construct( allOf=[oai.Reference.construct(ref="#/First"), oai.Reference.construct(ref="#/Second")] ) - prop = string_property() + prop = string_property_factory(nullable=True) schemas = Schemas( classes_by_reference={ "/First": model_property_factory(optional_properties=[prop]), @@ -299,7 +266,13 @@ def test_duplicate_properties(self, model_property_factory): @pytest.mark.parametrize("first_required", [True, False]) @pytest.mark.parametrize("second_required", [True, False]) def test_mixed_requirements( - self, model_property_factory, first_nullable, second_nullable, first_required, second_required + self, + model_property_factory, + first_nullable, + second_nullable, + first_required, + second_required, + string_property_factory, ): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties @@ -310,10 +283,10 @@ def test_mixed_requirements( schemas = Schemas( classes_by_reference={ "/First": model_property_factory( - optional_properties=[string_property(required=first_required, nullable=first_nullable)] + optional_properties=[string_property_factory(required=first_required, nullable=first_nullable)] ), "/Second": model_property_factory( - optional_properties=[string_property(required=second_required, nullable=second_nullable)] + optional_properties=[string_property_factory(required=second_required, nullable=second_nullable)] ), } ) @@ -322,7 +295,7 @@ def test_mixed_requirements( nullable = first_nullable and second_nullable required = first_required or second_required - expected_prop = string_property( + expected_prop = string_property_factory( nullable=nullable, required=required, ) @@ -332,7 +305,7 @@ def test_mixed_requirements( else: assert result.required_props == [expected_prop] - def test_direct_properties_non_ref(self): + def test_direct_properties_non_ref(self, string_property_factory): from openapi_python_client.parser.properties import Schemas from openapi_python_client.parser.properties.model_property import _process_properties @@ -351,5 +324,5 @@ def test_direct_properties_non_ref(self): result = _process_properties(data=data, schemas=schemas, class_name="", config=MagicMock()) - assert result.optional_props == [string_property(name="second", required=False, nullable=False)] - assert result.required_props == [string_property(name="first", required=True, nullable=False)] + assert result.optional_props == [string_property_factory(name="second", required=False, nullable=False)] + assert result.required_props == [string_property_factory(name="first", required=True, nullable=False)] diff --git a/tests/test_parser/test_properties/test_property.py b/tests/test_parser/test_properties/test_property.py new file mode 100644 index 000000000..00da3fe46 --- /dev/null +++ b/tests/test_parser/test_properties/test_property.py @@ -0,0 +1,62 @@ +import pytest + + +class TestProperty: + @pytest.mark.parametrize( + "nullable,required,no_optional,json,expected", + [ + (False, False, False, False, "Union[Unset, TestType]"), + (False, False, True, False, "TestType"), + (False, True, False, False, "TestType"), + (False, True, True, False, "TestType"), + (True, False, False, False, "Union[Unset, None, TestType]"), + (True, False, True, False, "TestType"), + (True, True, False, False, "Optional[TestType]"), + (True, True, True, False, "TestType"), + (False, False, False, True, "Union[Unset, str]"), + (False, False, True, True, "str"), + (False, True, False, True, "str"), + (False, True, True, True, "str"), + (True, False, False, True, "Union[Unset, None, str]"), + (True, False, True, True, "str"), + (True, True, False, True, "Optional[str]"), + (True, True, True, True, "str"), + ], + ) + def test_get_type_string(self, property_factory, mocker, nullable, required, no_optional, json, expected): + from openapi_python_client.parser.properties import Property + + mocker.patch.object(Property, "_type_string", "TestType") + mocker.patch.object(Property, "_json_type_string", "str") + p = property_factory(required=required, nullable=nullable) + assert p.get_type_string(no_optional=no_optional, json=json) == expected + + @pytest.mark.parametrize( + "default,required,expected", + [ + (None, False, "test: Union[Unset, TestType] = UNSET"), + (None, True, "test: TestType"), + ("Test", False, "test: Union[Unset, TestType] = Test"), + ("Test", True, "test: TestType = Test"), + ], + ) + def test_to_string(self, mocker, default, required, expected, property_factory): + name = "test" + mocker.patch("openapi_python_client.parser.properties.Property._type_string", "TestType") + p = property_factory(name=name, required=required, default=default) + + assert p.to_string() == expected + + def test_get_imports(self, property_factory): + p = property_factory() + assert p.get_imports(prefix="") == set() + + p = property_factory(name="test", required=False, default=None, nullable=False) + assert p.get_imports(prefix="") == {"from types import UNSET, Unset", "from typing import Union"} + + p = property_factory(name="test", required=False, default=None, nullable=True) + assert p.get_imports(prefix="") == { + "from types import UNSET, Unset", + "from typing import Optional", + "from typing import Union", + } diff --git a/tests/test_parser/test_responses.py b/tests/test_parser/test_responses.py index d262c8a0d..8c35cea1f 100644 --- a/tests/test_parser/test_responses.py +++ b/tests/test_parser/test_responses.py @@ -20,7 +20,7 @@ def test_response_from_data_no_content(): assert response == Response( status_code=200, - prop=AnyProperty(name="response_200", default=None, nullable=False, required=True), + prop=AnyProperty(name="response_200", default=None, nullable=False, required=True, python_name="response_200"), source="None", ) @@ -46,7 +46,7 @@ def test_response_from_data_no_content_schema(): assert response == Response( status_code=200, - prop=AnyProperty(name="response_200", default=None, nullable=False, required=True), + prop=AnyProperty(name="response_200", default=None, nullable=False, required=True, python_name="response_200"), source="None", ) @@ -70,10 +70,10 @@ def test_response_from_data_property_error(mocker): ) -def test_response_from_data_property(mocker): +def test_response_from_data_property(mocker, property_factory): from openapi_python_client.parser import responses - prop = StringProperty(name="prop", required=True, nullable=False, default=None) + prop = property_factory() property_from_data = mocker.patch.object(responses, "property_from_data", return_value=(prop, Schemas())) data = oai.Response.construct( description="", content={"application/json": oai.MediaType.construct(media_type_schema="something")} diff --git a/tests/test_templates/test_property_templates/test_date_property/test_date_property.py b/tests/test_templates/test_property_templates/test_date_property/test_date_property.py index b44e43d23..02137add4 100644 --- a/tests/test_templates/test_property_templates/test_date_property/test_date_property.py +++ b/tests/test_templates/test_property_templates/test_date_property/test_date_property.py @@ -2,16 +2,21 @@ import jinja2 +from openapi_python_client.parser.properties import DateProperty -def test_required_not_nullable(): - from openapi_python_client.parser.properties import DateProperty - prop = DateProperty( +def date_property(required=True, nullable=True, default=None) -> DateProperty: + return DateProperty( name="a_prop", - required=True, - nullable=False, - default=None, + required=required, + nullable=nullable, + default=default, + python_name="a_prop", ) + + +def test_required_not_nullable(): + prop = date_property(nullable=False) here = Path(__file__).parent templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates" @@ -28,14 +33,8 @@ def test_required_not_nullable(): def test_required_nullable(): - from openapi_python_client.parser.properties import DateProperty - prop = DateProperty( - name="a_prop", - required=True, - nullable=True, - default=None, - ) + prop = date_property() here = Path(__file__).parent templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates" @@ -52,14 +51,7 @@ def test_required_nullable(): def test_optional_nullable(): - from openapi_python_client.parser.properties import DateProperty - - prop = DateProperty( - name="a_prop", - required=False, - nullable=True, - default=None, - ) + prop = date_property(required=False) here = Path(__file__).parent templates_dir = here.parent.parent.parent.parent / "openapi_python_client" / "templates" diff --git a/tests/test_utils.py b/tests/test_utils.py index 9d3b57f4b..ec74accc2 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,6 +3,23 @@ from openapi_python_client import utils +class TestPythonIdentifier: + def test_valid_identifier_is_not_changed(self): + assert utils.PythonIdentifier(value="valid_field", prefix="field") == "valid_field" + + def test_numbers_are_prefixed(self): + assert utils.PythonIdentifier(value="1", prefix="field") == "field1" + + def test_invalid_symbols_are_stripped(self): + assert utils.PythonIdentifier(value="$abc", prefix="prefix") == "abc" + + def test_keywords_are_postfixed(self): + assert utils.PythonIdentifier(value="for", prefix="prefix") == "for_" + + def test_empty_is_prefixed(self): + assert utils.PythonIdentifier(value="", prefix="something") == "something" + + @pytest.mark.parametrize( "before, after", [ @@ -76,13 +93,6 @@ def test__fix_reserved_words(reserved_word: str, expected: str): assert utils.fix_reserved_words(reserved_word) == expected -def test_to_valid_python_identifier(): - assert utils.to_valid_python_identifier("valid") == "valid" - assert utils.to_valid_python_identifier("1") == "field_1" - assert utils.to_valid_python_identifier("$") == "field_" - assert utils.to_valid_python_identifier("for") == "for_" - - @pytest.mark.parametrize( "before, after", [