From 122260f27e71c7a26e44c8c2790c464a992af6b1 Mon Sep 17 00:00:00 2001 From: Jonathan Ballet Date: Sat, 30 Sep 2023 23:27:13 +0200 Subject: [PATCH] python: provide full typing for the REST client(s) --- .../samples-python-client-echo-api.yaml | 12 ++ .../workflows/samples-python-petstore.yaml | 13 ++ .../resources/python/asyncio/rest.mustache | 141 ++++++++++++----- .../main/resources/python/pyproject.mustache | 22 +++ .../src/main/resources/python/rest.mustache | 149 +++++++++++++----- .../python/test-requirements.mustache | 1 + .../openapi_client/rest.py | 149 +++++++++++++----- .../pyproject.toml | 22 +++ .../test-requirements.txt | 1 + .../echo_api/python/openapi_client/rest.py | 149 +++++++++++++----- samples/client/echo_api/python/pyproject.toml | 22 +++ .../echo_api/python/test-requirements.txt | 1 + .../python-aiohttp/petstore_api/rest.py | 141 ++++++++++++----- .../petstore/python-aiohttp/pyproject.toml | 22 +++ .../python-aiohttp/test-requirements.txt | 1 + .../petstore_api/rest.py | 149 ++++++++++++------ .../petstore/python/petstore_api/rest.py | 149 +++++++++++++----- .../client/petstore/python/pyproject.toml | 22 +++ .../petstore/python/test-requirements.txt | 1 + 19 files changed, 895 insertions(+), 272 deletions(-) diff --git a/.github/workflows/samples-python-client-echo-api.yaml b/.github/workflows/samples-python-client-echo-api.yaml index 38b1f24ad93b..870868ecd85f 100644 --- a/.github/workflows/samples-python-client-echo-api.yaml +++ b/.github/workflows/samples-python-client-echo-api.yaml @@ -42,3 +42,15 @@ jobs: - name: Test working-directory: ${{ matrix.sample }} run: python -m pytest + + # These modules should pass mypy without errors + - name: mypy (WIP) + working-directory: ${{ matrix.sample }} + run: | + python -m mypy */rest.py + + # This runs mypy on all the modules, but full typing is not yet fully implemented, this can fail. + - name: mypy + working-directory: ${{ matrix.sample }} + continue-on-error: true + run: python -m mypy diff --git a/.github/workflows/samples-python-petstore.yaml b/.github/workflows/samples-python-petstore.yaml index 21c80cc42db6..f2cf4077fa35 100644 --- a/.github/workflows/samples-python-petstore.yaml +++ b/.github/workflows/samples-python-petstore.yaml @@ -57,3 +57,16 @@ jobs: - name: Test working-directory: ${{ matrix.sample }} run: poetry run pytest -v + + # These modules should pass mypy without errors + - name: mypy (WIP) + working-directory: ${{ matrix.sample }} + run: | + poetry run mypy */rest.py + + # This runs mypy on all the modules, but full typing is not yet fully implemented, this can fail. + - name: mypy + working-directory: ${{ matrix.sample }} + continue-on-error: true + run: | + poetry run mypy diff --git a/modules/openapi-generator/src/main/resources/python/asyncio/rest.mustache b/modules/openapi-generator/src/main/resources/python/asyncio/rest.mustache index c97788d0c5cc..5e569cb68801 100644 --- a/modules/openapi-generator/src/main/resources/python/asyncio/rest.mustache +++ b/modules/openapi-generator/src/main/resources/python/asyncio/rest.mustache @@ -7,36 +7,43 @@ import json import logging import re import ssl +from typing import Any, Dict, List, Optional, Union, Tuple import aiohttp +from aiohttp import ClientResponse from urllib.parse import urlencode, quote_plus +from {{packageName}}.configuration import Configuration from {{packageName}}.exceptions import ApiException, ApiValueError logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp, data) -> None: - self.aiohttp_response = resp + def __init__(self, resp: ClientResponse, data: bytes) -> None: + self._aiohttp_response = resp self.status = resp.status self.reason = resp.reason self.data = data - def getheaders(self): - """Returns a CIMultiDictProxy of the response headers.""" - return self.aiohttp_response.headers + def getheaders(self) -> Dict[str, str]: + """Returns a dictionary of the response headers.""" + # Note: this can lose the CIMultiDictProxy duplicated headers. + return dict(self._aiohttp_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.aiohttp_response.headers.get(name, default) + return self._aiohttp_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: - + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # maxsize is number of requests to host that are allowed in parallel if maxsize is None: maxsize = configuration.connection_pool_maxsize @@ -65,12 +72,19 @@ class RESTClientObject: trust_env=True ) - async def close(self): + async def close(self) -> None: await self.pool_manager.close() - async def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + async def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: """Execute request :param method: http request method @@ -97,7 +111,7 @@ class RESTClientObject: "body parameter cannot be used with post_params parameter." ) - post_params = post_params or {} + post_params = post_params or [] headers = headers or {} # url already contains the URL query string # so reset query_params to empty dict @@ -108,8 +122,6 @@ class RESTClientObject: headers['Content-Type'] = 'application/json' args = { - "method": method, - "url": url, "timeout": timeout, "headers": headers } @@ -120,7 +132,8 @@ class RESTClientObject: args["proxy_headers"] = self.proxy_headers if query_params: - args["url"] += '?' + urlencode(query_params) + url += '?' + urlencode(query_params) + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: @@ -158,12 +171,12 @@ class RESTClientObject: declared content type.""" raise ApiException(status=0, reason=msg) - r = await self.pool_manager.request(**args) - if _preload_content: + _r = await self.pool_manager.request(method, url, **args) - data = await r.read() - r = RESTResponse(r, data) + response_data = await r.read() + r = RESTResponse(_r, response_data) + if _preload_content: # log response body logger.debug("response body: %s", r.data) @@ -172,25 +185,44 @@ class RESTClientObject: return r - async def get_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def head_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def options_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -199,8 +231,15 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body)) - async def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + async def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("DELETE", url, headers=headers, query_params=query_params, @@ -208,9 +247,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body)) - async def post_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("POST", url, headers=headers, query_params=query_params, @@ -219,8 +265,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body)) - async def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + async def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PUT", url, headers=headers, query_params=query_params, @@ -229,9 +283,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body)) - async def patch_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/modules/openapi-generator/src/main/resources/python/pyproject.mustache b/modules/openapi-generator/src/main/resources/python/pyproject.mustache index 568fa4ebf94e..2ead9348e5a4 100644 --- a/modules/openapi-generator/src/main/resources/python/pyproject.mustache +++ b/modules/openapi-generator/src/main/resources/python/pyproject.mustache @@ -31,6 +31,7 @@ typing-extensions = ">=4.7.1" pytest = ">=7.2.1" tox = ">=3.9.0" flake8 = ">=4.0.0" +mypy = "<1.5.0" # 1.5.0 supports Python 3.8+ [build-system] requires = ["setuptools"] @@ -38,3 +39,24 @@ build-backend = "setuptools.build_meta" [tool.pylint.'MESSAGES CONTROL'] extension-pkg-whitelist = "pydantic" + +[tool.mypy] +packages = ["{{packageName}}"] +warn_unused_configs = true +warn_redundant_casts = true + +[[tool.mypy.overrides]] +module = "{{packageName}}.rest" +#strict = true +warn_unused_ignores = true +strict_equality = true +strict_concatenate = true +check_untyped_defs = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_reexport = true +warn_return_any = true diff --git a/modules/openapi-generator/src/main/resources/python/rest.mustache b/modules/openapi-generator/src/main/resources/python/rest.mustache index f49dee2bed49..b60d49e61a3b 100644 --- a/modules/openapi-generator/src/main/resources/python/rest.mustache +++ b/modules/openapi-generator/src/main/resources/python/rest.mustache @@ -7,36 +7,51 @@ import json import logging import re import ssl +from typing import Any, Dict, Optional from urllib.parse import urlencode, quote_plus import urllib3 +from urllib3 import BaseHTTPResponse +from urllib3.util.timeout import _TYPE_TIMEOUT -from {{packageName}}.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException +from {{packageName}}.configuration import Configuration +from {{packageName}}.exceptions import ApiException +from {{packageName}}.exceptions import ApiValueError +from {{packageName}}.exceptions import BadRequestException +from {{packageName}}.exceptions import ForbiddenException +from {{packageName}}.exceptions import NotFoundException +from {{packageName}}.exceptions import ServiceException +from {{packageName}}.exceptions import UnauthorizedException logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp) -> None: - self.urllib3_response = resp + def __init__(self, resp: BaseHTTPResponse) -> None: + self._urllib3_response = resp self.status = resp.status self.reason = resp.reason self.data = resp.data - def getheaders(self): + def getheaders(self) -> Dict[str, str]: """Returns a dictionary of the response headers.""" - return self.urllib3_response.headers + # Note: this can lose the urllib3.HTTPHeaderDict duplicated headers. + return dict(self._urllib3_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.urllib3_response.headers.get(name, default) + return self._urllib3_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # urllib3.PoolManager will pass all kw parameters to connectionpool # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 @@ -49,7 +64,7 @@ class RESTClientObject: else: cert_reqs = ssl.CERT_NONE - addition_pool_args = {} + addition_pool_args: Dict[str, Any] = {} if configuration.assert_hostname is not None: addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 @@ -69,6 +84,8 @@ class RESTClientObject: else: maxsize = 4 + self.pool_manager: urllib3.PoolManager + # https pool manager if configuration.proxy: self.pool_manager = urllib3.ProxyManager( @@ -93,9 +110,16 @@ class RESTClientObject: **addition_pool_args ) - def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: """Perform requests. :param method: http request method @@ -147,14 +171,14 @@ class RESTClientObject: request_body = None if body is not None: request_body = json.dumps(body) - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, body=request_body, preload_content=_preload_content, timeout=timeout, headers=headers) elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=False, @@ -166,7 +190,7 @@ class RESTClientObject: # Content-Type which generated by urllib3 will be # overwritten. del headers['Content-Type'] - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=True, @@ -177,10 +201,9 @@ class RESTClientObject: # other content types than Json when `body` argument is # provided in serialized form elif isinstance(body, str) or isinstance(body, bytes): - request_body = body - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, - body=request_body, + body=body, preload_content=_preload_content, timeout=timeout, headers=headers) @@ -192,7 +215,7 @@ class RESTClientObject: raise ApiException(status=0, reason=msg) # For `GET`, `HEAD` else: - r = self.pool_manager.request(method, url, + _r = self.pool_manager.request(method, url, fields={}, preload_content=_preload_content, timeout=timeout, @@ -201,12 +224,13 @@ class RESTClientObject: msg = "{0}\n{1}".format(type(e).__name__, str(e)) raise ApiException(status=0, reason=msg) - if _preload_content: - r = RESTResponse(r) + r = RESTResponse(_r) + if _preload_content: # log response body logger.debug("response body: %s", r.data) + if not 200 <= r.status <= 299: if r.status == 400: raise BadRequestException(http_resp=r) @@ -227,24 +251,44 @@ class RESTClientObject: return r - def get_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def head_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def options_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -253,8 +297,15 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body) - def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("DELETE", url, headers=headers, query_params=query_params, @@ -262,8 +313,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body) - def post_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("POST", url, headers=headers, query_params=query_params, @@ -272,8 +331,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body) - def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PUT", url, headers=headers, query_params=query_params, @@ -282,8 +349,16 @@ class RESTClientObject: _request_timeout=_request_timeout, body=body) - def patch_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/modules/openapi-generator/src/main/resources/python/test-requirements.mustache b/modules/openapi-generator/src/main/resources/python/test-requirements.mustache index 3a0d0b939a1e..25f42f38aa47 100644 --- a/modules/openapi-generator/src/main/resources/python/test-requirements.mustache +++ b/modules/openapi-generator/src/main/resources/python/test-requirements.mustache @@ -1,3 +1,4 @@ pytest~=7.1.3 pytest-cov>=2.8.1 pytest-randomly>=3.12.0 +mypy<1.5.0 diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/openapi_client/rest.py b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/openapi_client/rest.py index d05e077260de..daf36109cd76 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/openapi_client/rest.py +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/openapi_client/rest.py @@ -18,36 +18,51 @@ import logging import re import ssl +from typing import Any, Dict, Optional from urllib.parse import urlencode, quote_plus import urllib3 +from urllib3 import BaseHTTPResponse +from urllib3.util.timeout import _TYPE_TIMEOUT -from openapi_client.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException +from openapi_client.configuration import Configuration +from openapi_client.exceptions import ApiException +from openapi_client.exceptions import ApiValueError +from openapi_client.exceptions import BadRequestException +from openapi_client.exceptions import ForbiddenException +from openapi_client.exceptions import NotFoundException +from openapi_client.exceptions import ServiceException +from openapi_client.exceptions import UnauthorizedException logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp) -> None: - self.urllib3_response = resp + def __init__(self, resp: BaseHTTPResponse) -> None: + self._urllib3_response = resp self.status = resp.status self.reason = resp.reason self.data = resp.data - def getheaders(self): + def getheaders(self) -> Dict[str, str]: """Returns a dictionary of the response headers.""" - return self.urllib3_response.headers + # Note: this can lose the urllib3.HTTPHeaderDict duplicated headers. + return dict(self._urllib3_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.urllib3_response.headers.get(name, default) + return self._urllib3_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # urllib3.PoolManager will pass all kw parameters to connectionpool # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 @@ -60,7 +75,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: cert_reqs = ssl.CERT_NONE - addition_pool_args = {} + addition_pool_args: Dict[str, Any] = {} if configuration.assert_hostname is not None: addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 @@ -80,6 +95,8 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: maxsize = 4 + self.pool_manager: urllib3.PoolManager + # https pool manager if configuration.proxy: self.pool_manager = urllib3.ProxyManager( @@ -104,9 +121,16 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: **addition_pool_args ) - def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: """Perform requests. :param method: http request method @@ -158,14 +182,14 @@ def request(self, method, url, query_params=None, headers=None, request_body = None if body is not None: request_body = json.dumps(body) - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, body=request_body, preload_content=_preload_content, timeout=timeout, headers=headers) elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=False, @@ -177,7 +201,7 @@ def request(self, method, url, query_params=None, headers=None, # Content-Type which generated by urllib3 will be # overwritten. del headers['Content-Type'] - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=True, @@ -188,10 +212,9 @@ def request(self, method, url, query_params=None, headers=None, # other content types than Json when `body` argument is # provided in serialized form elif isinstance(body, str) or isinstance(body, bytes): - request_body = body - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, - body=request_body, + body=body, preload_content=_preload_content, timeout=timeout, headers=headers) @@ -203,7 +226,7 @@ def request(self, method, url, query_params=None, headers=None, raise ApiException(status=0, reason=msg) # For `GET`, `HEAD` else: - r = self.pool_manager.request(method, url, + _r = self.pool_manager.request(method, url, fields={}, preload_content=_preload_content, timeout=timeout, @@ -212,12 +235,13 @@ def request(self, method, url, query_params=None, headers=None, msg = "{0}\n{1}".format(type(e).__name__, str(e)) raise ApiException(status=0, reason=msg) - if _preload_content: - r = RESTResponse(r) + r = RESTResponse(_r) + if _preload_content: # log response body logger.debug("response body: %s", r.data) + if not 200 <= r.status <= 299: if r.status == 400: raise BadRequestException(http_resp=r) @@ -238,24 +262,44 @@ def request(self, method, url, query_params=None, headers=None, return r - def get_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def head_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def options_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -264,8 +308,15 @@ def options_request(self, url, headers=None, query_params=None, post_params=None _request_timeout=_request_timeout, body=body) - def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("DELETE", url, headers=headers, query_params=query_params, @@ -273,8 +324,16 @@ def delete_request(self, url, headers=None, query_params=None, body=None, _request_timeout=_request_timeout, body=body) - def post_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("POST", url, headers=headers, query_params=query_params, @@ -283,8 +342,16 @@ def post_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PUT", url, headers=headers, query_params=query_params, @@ -293,8 +360,16 @@ def put_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def patch_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/pyproject.toml b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/pyproject.toml index af471ab63148..c3db77d568aa 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/pyproject.toml +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/pyproject.toml @@ -21,6 +21,7 @@ typing-extensions = ">=4.7.1" pytest = ">=7.2.1" tox = ">=3.9.0" flake8 = ">=4.0.0" +mypy = "<1.5.0" # 1.5.0 supports Python 3.8+ [build-system] requires = ["setuptools"] @@ -28,3 +29,24 @@ build-backend = "setuptools.build_meta" [tool.pylint.'MESSAGES CONTROL'] extension-pkg-whitelist = "pydantic" + +[tool.mypy] +packages = ["openapi_client"] +warn_unused_configs = true +warn_redundant_casts = true + +[[tool.mypy.overrides]] +module = "openapi_client.rest" +#strict = true +warn_unused_ignores = true +strict_equality = true +strict_concatenate = true +check_untyped_defs = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_reexport = true +warn_return_any = true diff --git a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/test-requirements.txt b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/test-requirements.txt index 3a0d0b939a1e..25f42f38aa47 100644 --- a/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/test-requirements.txt +++ b/samples/client/echo_api/python-disallowAdditionalPropertiesIfNotPresent-true/test-requirements.txt @@ -1,3 +1,4 @@ pytest~=7.1.3 pytest-cov>=2.8.1 pytest-randomly>=3.12.0 +mypy<1.5.0 diff --git a/samples/client/echo_api/python/openapi_client/rest.py b/samples/client/echo_api/python/openapi_client/rest.py index d05e077260de..daf36109cd76 100644 --- a/samples/client/echo_api/python/openapi_client/rest.py +++ b/samples/client/echo_api/python/openapi_client/rest.py @@ -18,36 +18,51 @@ import logging import re import ssl +from typing import Any, Dict, Optional from urllib.parse import urlencode, quote_plus import urllib3 +from urllib3 import BaseHTTPResponse +from urllib3.util.timeout import _TYPE_TIMEOUT -from openapi_client.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException +from openapi_client.configuration import Configuration +from openapi_client.exceptions import ApiException +from openapi_client.exceptions import ApiValueError +from openapi_client.exceptions import BadRequestException +from openapi_client.exceptions import ForbiddenException +from openapi_client.exceptions import NotFoundException +from openapi_client.exceptions import ServiceException +from openapi_client.exceptions import UnauthorizedException logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp) -> None: - self.urllib3_response = resp + def __init__(self, resp: BaseHTTPResponse) -> None: + self._urllib3_response = resp self.status = resp.status self.reason = resp.reason self.data = resp.data - def getheaders(self): + def getheaders(self) -> Dict[str, str]: """Returns a dictionary of the response headers.""" - return self.urllib3_response.headers + # Note: this can lose the urllib3.HTTPHeaderDict duplicated headers. + return dict(self._urllib3_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.urllib3_response.headers.get(name, default) + return self._urllib3_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # urllib3.PoolManager will pass all kw parameters to connectionpool # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 @@ -60,7 +75,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: cert_reqs = ssl.CERT_NONE - addition_pool_args = {} + addition_pool_args: Dict[str, Any] = {} if configuration.assert_hostname is not None: addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 @@ -80,6 +95,8 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: maxsize = 4 + self.pool_manager: urllib3.PoolManager + # https pool manager if configuration.proxy: self.pool_manager = urllib3.ProxyManager( @@ -104,9 +121,16 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: **addition_pool_args ) - def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: """Perform requests. :param method: http request method @@ -158,14 +182,14 @@ def request(self, method, url, query_params=None, headers=None, request_body = None if body is not None: request_body = json.dumps(body) - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, body=request_body, preload_content=_preload_content, timeout=timeout, headers=headers) elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=False, @@ -177,7 +201,7 @@ def request(self, method, url, query_params=None, headers=None, # Content-Type which generated by urllib3 will be # overwritten. del headers['Content-Type'] - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=True, @@ -188,10 +212,9 @@ def request(self, method, url, query_params=None, headers=None, # other content types than Json when `body` argument is # provided in serialized form elif isinstance(body, str) or isinstance(body, bytes): - request_body = body - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, - body=request_body, + body=body, preload_content=_preload_content, timeout=timeout, headers=headers) @@ -203,7 +226,7 @@ def request(self, method, url, query_params=None, headers=None, raise ApiException(status=0, reason=msg) # For `GET`, `HEAD` else: - r = self.pool_manager.request(method, url, + _r = self.pool_manager.request(method, url, fields={}, preload_content=_preload_content, timeout=timeout, @@ -212,12 +235,13 @@ def request(self, method, url, query_params=None, headers=None, msg = "{0}\n{1}".format(type(e).__name__, str(e)) raise ApiException(status=0, reason=msg) - if _preload_content: - r = RESTResponse(r) + r = RESTResponse(_r) + if _preload_content: # log response body logger.debug("response body: %s", r.data) + if not 200 <= r.status <= 299: if r.status == 400: raise BadRequestException(http_resp=r) @@ -238,24 +262,44 @@ def request(self, method, url, query_params=None, headers=None, return r - def get_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def head_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def options_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -264,8 +308,15 @@ def options_request(self, url, headers=None, query_params=None, post_params=None _request_timeout=_request_timeout, body=body) - def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("DELETE", url, headers=headers, query_params=query_params, @@ -273,8 +324,16 @@ def delete_request(self, url, headers=None, query_params=None, body=None, _request_timeout=_request_timeout, body=body) - def post_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("POST", url, headers=headers, query_params=query_params, @@ -283,8 +342,16 @@ def post_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PUT", url, headers=headers, query_params=query_params, @@ -293,8 +360,16 @@ def put_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def patch_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/samples/client/echo_api/python/pyproject.toml b/samples/client/echo_api/python/pyproject.toml index af471ab63148..c3db77d568aa 100644 --- a/samples/client/echo_api/python/pyproject.toml +++ b/samples/client/echo_api/python/pyproject.toml @@ -21,6 +21,7 @@ typing-extensions = ">=4.7.1" pytest = ">=7.2.1" tox = ">=3.9.0" flake8 = ">=4.0.0" +mypy = "<1.5.0" # 1.5.0 supports Python 3.8+ [build-system] requires = ["setuptools"] @@ -28,3 +29,24 @@ build-backend = "setuptools.build_meta" [tool.pylint.'MESSAGES CONTROL'] extension-pkg-whitelist = "pydantic" + +[tool.mypy] +packages = ["openapi_client"] +warn_unused_configs = true +warn_redundant_casts = true + +[[tool.mypy.overrides]] +module = "openapi_client.rest" +#strict = true +warn_unused_ignores = true +strict_equality = true +strict_concatenate = true +check_untyped_defs = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_reexport = true +warn_return_any = true diff --git a/samples/client/echo_api/python/test-requirements.txt b/samples/client/echo_api/python/test-requirements.txt index 3a0d0b939a1e..25f42f38aa47 100644 --- a/samples/client/echo_api/python/test-requirements.txt +++ b/samples/client/echo_api/python/test-requirements.txt @@ -1,3 +1,4 @@ pytest~=7.1.3 pytest-cov>=2.8.1 pytest-randomly>=3.12.0 +mypy<1.5.0 diff --git a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/rest.py b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/rest.py index 7daf8c921c84..1749445a4064 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/petstore_api/rest.py +++ b/samples/openapi3/client/petstore/python-aiohttp/petstore_api/rest.py @@ -17,36 +17,43 @@ import logging import re import ssl +from typing import Any, Dict, List, Optional, Union, Tuple import aiohttp +from aiohttp import ClientResponse from urllib.parse import urlencode, quote_plus +from petstore_api.configuration import Configuration from petstore_api.exceptions import ApiException, ApiValueError logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp, data) -> None: - self.aiohttp_response = resp + def __init__(self, resp: ClientResponse, data: bytes) -> None: + self._aiohttp_response = resp self.status = resp.status self.reason = resp.reason self.data = data - def getheaders(self): - """Returns a CIMultiDictProxy of the response headers.""" - return self.aiohttp_response.headers + def getheaders(self) -> Dict[str, str]: + """Returns a dictionary of the response headers.""" + # Note: this can lose the CIMultiDictProxy duplicated headers. + return dict(self._aiohttp_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.aiohttp_response.headers.get(name, default) + return self._aiohttp_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: - + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # maxsize is number of requests to host that are allowed in parallel if maxsize is None: maxsize = configuration.connection_pool_maxsize @@ -75,12 +82,19 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: trust_env=True ) - async def close(self): + async def close(self) -> None: await self.pool_manager.close() - async def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + async def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: """Execute request :param method: http request method @@ -107,7 +121,7 @@ async def request(self, method, url, query_params=None, headers=None, "body parameter cannot be used with post_params parameter." ) - post_params = post_params or {} + post_params = post_params or [] headers = headers or {} # url already contains the URL query string # so reset query_params to empty dict @@ -118,8 +132,6 @@ async def request(self, method, url, query_params=None, headers=None, headers['Content-Type'] = 'application/json' args = { - "method": method, - "url": url, "timeout": timeout, "headers": headers } @@ -130,7 +142,8 @@ async def request(self, method, url, query_params=None, headers=None, args["proxy_headers"] = self.proxy_headers if query_params: - args["url"] += '?' + urlencode(query_params) + url += '?' + urlencode(query_params) + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: @@ -168,12 +181,12 @@ async def request(self, method, url, query_params=None, headers=None, declared content type.""" raise ApiException(status=0, reason=msg) - r = await self.pool_manager.request(**args) - if _preload_content: + _r = await self.pool_manager.request(method, url, **args) - data = await r.read() - r = RESTResponse(r, data) + response_data = await r.read() + r = RESTResponse(_r, response_data) + if _preload_content: # log response body logger.debug("response body: %s", r.data) @@ -182,25 +195,44 @@ async def request(self, method, url, query_params=None, headers=None, return r - async def get_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def head_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def options_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -209,8 +241,15 @@ async def options_request(self, url, headers=None, query_params=None, _request_timeout=_request_timeout, body=body)) - async def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + async def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("DELETE", url, headers=headers, query_params=query_params, @@ -218,9 +257,16 @@ async def delete_request(self, url, headers=None, query_params=None, body=None, _request_timeout=_request_timeout, body=body)) - async def post_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("POST", url, headers=headers, query_params=query_params, @@ -229,8 +275,16 @@ async def post_request(self, url, headers=None, query_params=None, _request_timeout=_request_timeout, body=body)) - async def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + async def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PUT", url, headers=headers, query_params=query_params, @@ -239,9 +293,16 @@ async def put_request(self, url, headers=None, query_params=None, post_params=No _request_timeout=_request_timeout, body=body)) - async def patch_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/samples/openapi3/client/petstore/python-aiohttp/pyproject.toml b/samples/openapi3/client/petstore/python-aiohttp/pyproject.toml index 6747ce2b9135..339f73daeb97 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/pyproject.toml +++ b/samples/openapi3/client/petstore/python-aiohttp/pyproject.toml @@ -24,6 +24,7 @@ typing-extensions = ">=4.7.1" pytest = ">=7.2.1" tox = ">=3.9.0" flake8 = ">=4.0.0" +mypy = "<1.5.0" # 1.5.0 supports Python 3.8+ [build-system] requires = ["setuptools"] @@ -31,3 +32,24 @@ build-backend = "setuptools.build_meta" [tool.pylint.'MESSAGES CONTROL'] extension-pkg-whitelist = "pydantic" + +[tool.mypy] +packages = ["petstore_api"] +warn_unused_configs = true +warn_redundant_casts = true + +[[tool.mypy.overrides]] +module = "petstore_api.rest" +#strict = true +warn_unused_ignores = true +strict_equality = true +strict_concatenate = true +check_untyped_defs = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_reexport = true +warn_return_any = true diff --git a/samples/openapi3/client/petstore/python-aiohttp/test-requirements.txt b/samples/openapi3/client/petstore/python-aiohttp/test-requirements.txt index 3a0d0b939a1e..25f42f38aa47 100644 --- a/samples/openapi3/client/petstore/python-aiohttp/test-requirements.txt +++ b/samples/openapi3/client/petstore/python-aiohttp/test-requirements.txt @@ -1,3 +1,4 @@ pytest~=7.1.3 pytest-cov>=2.8.1 pytest-randomly>=3.12.0 +mypy<1.5.0 diff --git a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/rest.py b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/rest.py index 7daf8c921c84..f5ff5113b29e 100644 --- a/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/rest.py +++ b/samples/openapi3/client/petstore/python-pydantic-v1-aiohttp/petstore_api/rest.py @@ -17,36 +17,43 @@ import logging import re import ssl +from typing import Any, Dict, List, Optional, Union, Tuple import aiohttp +from aiohttp import ClientResponse from urllib.parse import urlencode, quote_plus +from petstore_api.configuration import Configuration from petstore_api.exceptions import ApiException, ApiValueError logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp, data) -> None: - self.aiohttp_response = resp + def __init__(self, resp: ClientResponse, data: bytes) -> None: + self._aiohttp_response = resp self.status = resp.status self.reason = resp.reason self.data = data - def getheaders(self): - """Returns a CIMultiDictProxy of the response headers.""" - return self.aiohttp_response.headers + def getheaders(self) -> Dict[str, str]: + """Returns a dictionary of the response headers.""" + # Note: this can lose the CIMultiDictProxy duplicated headers. + return dict(self._aiohttp_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.aiohttp_response.headers.get(name, default) + return self._aiohttp_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: - + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # maxsize is number of requests to host that are allowed in parallel if maxsize is None: maxsize = configuration.connection_pool_maxsize @@ -75,12 +82,19 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: trust_env=True ) - async def close(self): + async def close(self) -> None: await self.pool_manager.close() - async def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + async def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: """Execute request :param method: http request method @@ -107,7 +121,7 @@ async def request(self, method, url, query_params=None, headers=None, "body parameter cannot be used with post_params parameter." ) - post_params = post_params or {} + post_params = post_params or [] headers = headers or {} # url already contains the URL query string # so reset query_params to empty dict @@ -118,8 +132,6 @@ async def request(self, method, url, query_params=None, headers=None, headers['Content-Type'] = 'application/json' args = { - "method": method, - "url": url, "timeout": timeout, "headers": headers } @@ -130,7 +142,8 @@ async def request(self, method, url, query_params=None, headers=None, args["proxy_headers"] = self.proxy_headers if query_params: - args["url"] += '?' + urlencode(query_params) + url += '?' + urlencode(query_params) + # For `POST`, `PUT`, `PATCH`, `OPTIONS`, `DELETE` if method in ['POST', 'PUT', 'PATCH', 'OPTIONS', 'DELETE']: @@ -168,39 +181,58 @@ async def request(self, method, url, query_params=None, headers=None, declared content type.""" raise ApiException(status=0, reason=msg) - r = await self.pool_manager.request(**args) - if _preload_content: + r = await self.pool_manager.request(method, url, **args) - data = await r.read() - r = RESTResponse(r, data) + response_data = await r.read() + response = RESTResponse(r, response_data) + if _preload_content: # log response body - logger.debug("response body: %s", r.data) + logger.debug("response body: %s", response.data) - if not 200 <= r.status <= 299: - raise ApiException(http_resp=r) + if not 200 <= r.status <= 299: + raise ApiException(http_resp=response) - return r + return response - async def get_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def head_request(self, url, headers=None, query_params=None, - _preload_content=True, _request_timeout=None): + async def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params)) - async def options_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -209,8 +241,15 @@ async def options_request(self, url, headers=None, query_params=None, _request_timeout=_request_timeout, body=body)) - async def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + async def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("DELETE", url, headers=headers, query_params=query_params, @@ -218,9 +257,16 @@ async def delete_request(self, url, headers=None, query_params=None, body=None, _request_timeout=_request_timeout, body=body)) - async def post_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("POST", url, headers=headers, query_params=query_params, @@ -229,8 +275,16 @@ async def post_request(self, url, headers=None, query_params=None, _request_timeout=_request_timeout, body=body)) - async def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + async def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PUT", url, headers=headers, query_params=query_params, @@ -239,9 +293,16 @@ async def put_request(self, url, headers=None, query_params=None, post_params=No _request_timeout=_request_timeout, body=body)) - async def patch_request(self, url, headers=None, query_params=None, - post_params=None, body=None, _preload_content=True, - _request_timeout=None): + async def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[List[Tuple[str, Union[str, Tuple[str, bytes, str]]]]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: Optional[int]=None, + ) -> RESTResponse: return (await self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/samples/openapi3/client/petstore/python/petstore_api/rest.py b/samples/openapi3/client/petstore/python/petstore_api/rest.py index df9aca008048..efd95991078b 100755 --- a/samples/openapi3/client/petstore/python/petstore_api/rest.py +++ b/samples/openapi3/client/petstore/python/petstore_api/rest.py @@ -17,36 +17,51 @@ import logging import re import ssl +from typing import Any, Dict, Optional from urllib.parse import urlencode, quote_plus import urllib3 +from urllib3 import BaseHTTPResponse +from urllib3.util.timeout import _TYPE_TIMEOUT -from petstore_api.exceptions import ApiException, UnauthorizedException, ForbiddenException, NotFoundException, ServiceException, ApiValueError, BadRequestException +from petstore_api.configuration import Configuration +from petstore_api.exceptions import ApiException +from petstore_api.exceptions import ApiValueError +from petstore_api.exceptions import BadRequestException +from petstore_api.exceptions import ForbiddenException +from petstore_api.exceptions import NotFoundException +from petstore_api.exceptions import ServiceException +from petstore_api.exceptions import UnauthorizedException logger = logging.getLogger(__name__) -class RESTResponse(io.IOBase): +class RESTResponse: + """An HTTP response.""" + # This provides a generic object to store HTTP responses. + # It proxies the original HTTP response from the underlying HTTP library + # (aiohttp, urllib3, etc.) so that clients of RESTClientObject can work + # without knowing too much about each library specifics. - def __init__(self, resp) -> None: - self.urllib3_response = resp + def __init__(self, resp: BaseHTTPResponse) -> None: + self._urllib3_response = resp self.status = resp.status self.reason = resp.reason self.data = resp.data - def getheaders(self): + def getheaders(self) -> Dict[str, str]: """Returns a dictionary of the response headers.""" - return self.urllib3_response.headers + # Note: this can lose the urllib3.HTTPHeaderDict duplicated headers. + return dict(self._urllib3_response.headers) - def getheader(self, name, default=None): + def getheader(self, name: str, default: Optional[str]=None) -> Optional[str]: """Returns a given response header.""" - return self.urllib3_response.headers.get(name, default) + return self._urllib3_response.headers.get(name, default) class RESTClientObject: - - def __init__(self, configuration, pools_size=4, maxsize=None) -> None: + def __init__(self, configuration: Configuration, pools_size: int=4, maxsize: Optional[int]=None) -> None: # urllib3.PoolManager will pass all kw parameters to connectionpool # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/poolmanager.py#L75 # noqa: E501 # https://github.com/shazow/urllib3/blob/f9409436f83aeb79fbaf090181cd81b784f1b8ce/urllib3/connectionpool.py#L680 # noqa: E501 @@ -59,7 +74,7 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: cert_reqs = ssl.CERT_NONE - addition_pool_args = {} + addition_pool_args: Dict[str, Any] = {} if configuration.assert_hostname is not None: addition_pool_args['assert_hostname'] = configuration.assert_hostname # noqa: E501 @@ -79,6 +94,8 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: else: maxsize = 4 + self.pool_manager: urllib3.PoolManager + # https pool manager if configuration.proxy: self.pool_manager = urllib3.ProxyManager( @@ -103,9 +120,16 @@ def __init__(self, configuration, pools_size=4, maxsize=None) -> None: **addition_pool_args ) - def request(self, method, url, query_params=None, headers=None, - body=None, post_params=None, _preload_content=True, - _request_timeout=None): + def request(self, + method: str, + url: str, + query_params: Optional[Dict[str, str]]=None, + headers: Optional[Dict[str, str]]=None, + body: Any=None, + post_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: """Perform requests. :param method: http request method @@ -157,14 +181,14 @@ def request(self, method, url, query_params=None, headers=None, request_body = None if body is not None: request_body = json.dumps(body) - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, body=request_body, preload_content=_preload_content, timeout=timeout, headers=headers) elif headers['Content-Type'] == 'application/x-www-form-urlencoded': # noqa: E501 - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=False, @@ -176,7 +200,7 @@ def request(self, method, url, query_params=None, headers=None, # Content-Type which generated by urllib3 will be # overwritten. del headers['Content-Type'] - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, fields=post_params, encode_multipart=True, @@ -187,10 +211,9 @@ def request(self, method, url, query_params=None, headers=None, # other content types than Json when `body` argument is # provided in serialized form elif isinstance(body, str) or isinstance(body, bytes): - request_body = body - r = self.pool_manager.request( + _r = self.pool_manager.request( method, url, - body=request_body, + body=body, preload_content=_preload_content, timeout=timeout, headers=headers) @@ -202,7 +225,7 @@ def request(self, method, url, query_params=None, headers=None, raise ApiException(status=0, reason=msg) # For `GET`, `HEAD` else: - r = self.pool_manager.request(method, url, + _r = self.pool_manager.request(method, url, fields={}, preload_content=_preload_content, timeout=timeout, @@ -211,12 +234,13 @@ def request(self, method, url, query_params=None, headers=None, msg = "{0}\n{1}".format(type(e).__name__, str(e)) raise ApiException(status=0, reason=msg) - if _preload_content: - r = RESTResponse(r) + r = RESTResponse(_r) + if _preload_content: # log response body logger.debug("response body: %s", r.data) + if not 200 <= r.status <= 299: if r.status == 400: raise BadRequestException(http_resp=r) @@ -237,24 +261,44 @@ def request(self, method, url, query_params=None, headers=None, return r - def get_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def get_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("GET", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def head_request(self, url, headers=None, query_params=None, _preload_content=True, - _request_timeout=None): + def head_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("HEAD", url, headers=headers, _preload_content=_preload_content, _request_timeout=_request_timeout, query_params=query_params) - def options_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def options_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("OPTIONS", url, headers=headers, query_params=query_params, @@ -263,8 +307,15 @@ def options_request(self, url, headers=None, query_params=None, post_params=None _request_timeout=_request_timeout, body=body) - def delete_request(self, url, headers=None, query_params=None, body=None, - _preload_content=True, _request_timeout=None): + def delete_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("DELETE", url, headers=headers, query_params=query_params, @@ -272,8 +323,16 @@ def delete_request(self, url, headers=None, query_params=None, body=None, _request_timeout=_request_timeout, body=body) - def post_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def post_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("POST", url, headers=headers, query_params=query_params, @@ -282,8 +341,16 @@ def post_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def put_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def put_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PUT", url, headers=headers, query_params=query_params, @@ -292,8 +359,16 @@ def put_request(self, url, headers=None, query_params=None, post_params=None, _request_timeout=_request_timeout, body=body) - def patch_request(self, url, headers=None, query_params=None, post_params=None, - body=None, _preload_content=True, _request_timeout=None): + def patch_request( + self, + url: str, + headers: Optional[Dict[str, str]]=None, + query_params: Optional[Dict[str, str]]=None, + post_params: Optional[Dict[str, str]]=None, + body: Any=None, + _preload_content: bool=True, + _request_timeout: _TYPE_TIMEOUT=None, + ) -> RESTResponse: return self.request("PATCH", url, headers=headers, query_params=query_params, diff --git a/samples/openapi3/client/petstore/python/pyproject.toml b/samples/openapi3/client/petstore/python/pyproject.toml index 0d153fcb0c3d..de27d15fba0d 100644 --- a/samples/openapi3/client/petstore/python/pyproject.toml +++ b/samples/openapi3/client/petstore/python/pyproject.toml @@ -23,6 +23,7 @@ typing-extensions = ">=4.7.1" pytest = ">=7.2.1" tox = ">=3.9.0" flake8 = ">=4.0.0" +mypy = "<1.5.0" # 1.5.0 supports Python 3.8+ [build-system] requires = ["setuptools"] @@ -30,3 +31,24 @@ build-backend = "setuptools.build_meta" [tool.pylint.'MESSAGES CONTROL'] extension-pkg-whitelist = "pydantic" + +[tool.mypy] +packages = ["petstore_api"] +warn_unused_configs = true +warn_redundant_casts = true + +[[tool.mypy.overrides]] +module = "petstore_api.rest" +#strict = true +warn_unused_ignores = true +strict_equality = true +strict_concatenate = true +check_untyped_defs = true +disallow_subclassing_any = true +disallow_untyped_decorators = true +disallow_any_generics = true +disallow_untyped_calls = true +disallow_incomplete_defs = true +disallow_untyped_defs = true +no_implicit_reexport = true +warn_return_any = true diff --git a/samples/openapi3/client/petstore/python/test-requirements.txt b/samples/openapi3/client/petstore/python/test-requirements.txt index 3a0d0b939a1e..25f42f38aa47 100755 --- a/samples/openapi3/client/petstore/python/test-requirements.txt +++ b/samples/openapi3/client/petstore/python/test-requirements.txt @@ -1,3 +1,4 @@ pytest~=7.1.3 pytest-cov>=2.8.1 pytest-randomly>=3.12.0 +mypy<1.5.0