python: stronger typing for the "configuration" module (#20014)

* python: strong typing for the configuration module

* 3.8 compatibility

* fix bearer format

* Specific auth settings
This commit is contained in:
Jonathan Ballet
2024-11-06 09:36:47 +01:00
committed by GitHub
parent 54920ff106
commit 57cfff1950
11 changed files with 1264 additions and 521 deletions

View File

@@ -13,13 +13,16 @@
import copy
import http.client as httplib
import logging
from logging import FileHandler
import sys
from typing import Optional
from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict
from typing_extensions import NotRequired, Self
import urllib3
import http.client as httplib
from petstore_api.signing import HttpSigningConfiguration
JSON_SCHEMA_VALIDATION_KEYWORDS = {
'multipleOf', 'maximum', 'exclusiveMaximum',
@@ -27,6 +30,112 @@ JSON_SCHEMA_VALIDATION_KEYWORDS = {
'minLength', 'pattern', 'maxItems', 'minItems'
}
ServerVariablesT = Dict[str, str]
GenericAuthSetting = TypedDict(
"GenericAuthSetting",
{
"type": str,
"in": str,
"key": str,
"value": str,
},
)
OAuth2AuthSetting = TypedDict(
"OAuth2AuthSetting",
{
"type": Literal["oauth2"],
"in": Literal["header"],
"key": Literal["Authorization"],
"value": str,
},
)
APIKeyAuthSetting = TypedDict(
"APIKeyAuthSetting",
{
"type": Literal["api_key"],
"in": str,
"key": str,
"value": Optional[str],
},
)
BasicAuthSetting = TypedDict(
"BasicAuthSetting",
{
"type": Literal["basic"],
"in": Literal["header"],
"key": Literal["Authorization"],
"value": Optional[str],
},
)
BearerFormatAuthSetting = TypedDict(
"BearerFormatAuthSetting",
{
"type": Literal["bearer"],
"in": Literal["header"],
"format": Literal["JWT"],
"key": Literal["Authorization"],
"value": str,
},
)
BearerAuthSetting = TypedDict(
"BearerAuthSetting",
{
"type": Literal["bearer"],
"in": Literal["header"],
"key": Literal["Authorization"],
"value": str,
},
)
HTTPSignatureAuthSetting = TypedDict(
"HTTPSignatureAuthSetting",
{
"type": Literal["http-signature"],
"in": Literal["header"],
"key": Literal["Authorization"],
"value": None,
},
)
AuthSettings = TypedDict(
"AuthSettings",
{
"petstore_auth": OAuth2AuthSetting,
"api_key": APIKeyAuthSetting,
"api_key_query": APIKeyAuthSetting,
"http_basic_test": BasicAuthSetting,
"bearer_test": BearerFormatAuthSetting,
"http_signature_test": HTTPSignatureAuthSetting,
},
total=False,
)
class HostSettingVariable(TypedDict):
description: str
default_value: str
enum_values: List[str]
class HostSetting(TypedDict):
url: str
description: str
variables: NotRequired[Dict[str, HostSettingVariable]]
class Configuration:
"""This class contains various settings of the API client.
@@ -137,21 +246,27 @@ conf = petstore_api.Configuration(
)
"""
_default = None
_default: ClassVar[Optional[Self]] = None
def __init__(self, host=None,
api_key=None, api_key_prefix=None,
username=None, password=None,
access_token=None,
signing_info=None,
server_index=None, server_variables=None,
server_operation_index=None, server_operation_variables=None,
ignore_operation_servers=False,
ssl_ca_cert=None,
retries=None,
*,
debug: Optional[bool] = None
) -> None:
def __init__(
self,
host: Optional[str]=None,
api_key: Optional[Dict[str, str]]=None,
api_key_prefix: Optional[Dict[str, str]]=None,
username: Optional[str]=None,
password: Optional[str]=None,
access_token: Optional[str]=None,
signing_info: Optional[HttpSigningConfiguration]=None,
server_index: Optional[int]=None,
server_variables: Optional[ServerVariablesT]=None,
server_operation_index: Optional[Dict[int, int]]=None,
server_operation_variables: Optional[Dict[int, ServerVariablesT]]=None,
ignore_operation_servers: bool=False,
ssl_ca_cert: Optional[str]=None,
retries: Optional[int] = None,
*,
debug: Optional[bool] = None,
) -> None:
"""Constructor
"""
self._base_path = "http://petstore.swagger.io:80/v2" if host is None else host
@@ -277,7 +392,7 @@ conf = petstore_api.Configuration(
"""date format
"""
def __deepcopy__(self, memo):
def __deepcopy__(self, memo: Dict[int, Any]) -> Self:
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
@@ -291,7 +406,7 @@ conf = petstore_api.Configuration(
result.debug = self.debug
return result
def __setattr__(self, name, value):
def __setattr__(self, name: str, value: Any) -> None:
object.__setattr__(self, name, value)
if name == "signing_info" and value is not None:
# Ensure the host parameter from signing info is the same as
@@ -299,7 +414,7 @@ conf = petstore_api.Configuration(
value.host = self.host
@classmethod
def set_default(cls, default):
def set_default(cls, default: Optional[Self]) -> None:
"""Set default instance of configuration.
It stores default configuration, which can be
@@ -310,7 +425,7 @@ conf = petstore_api.Configuration(
cls._default = default
@classmethod
def get_default_copy(cls):
def get_default_copy(cls) -> Self:
"""Deprecated. Please use `get_default` instead.
Deprecated. Please use `get_default` instead.
@@ -320,7 +435,7 @@ conf = petstore_api.Configuration(
return cls.get_default()
@classmethod
def get_default(cls):
def get_default(cls) -> Self:
"""Return the default configuration.
This method returns newly created, based on default constructor,
@@ -330,11 +445,11 @@ conf = petstore_api.Configuration(
:return: The configuration object.
"""
if cls._default is None:
cls._default = Configuration()
cls._default = cls()
return cls._default
@property
def logger_file(self):
def logger_file(self) -> Optional[str]:
"""The logger file.
If the logger_file is None, then add stream handler and remove file
@@ -346,7 +461,7 @@ conf = petstore_api.Configuration(
return self.__logger_file
@logger_file.setter
def logger_file(self, value):
def logger_file(self, value: Optional[str]) -> None:
"""The logger file.
If the logger_file is None, then add stream handler and remove file
@@ -365,7 +480,7 @@ conf = petstore_api.Configuration(
logger.addHandler(self.logger_file_handler)
@property
def debug(self):
def debug(self) -> bool:
"""Debug status
:param value: The debug status, True or False.
@@ -374,7 +489,7 @@ conf = petstore_api.Configuration(
return self.__debug
@debug.setter
def debug(self, value):
def debug(self, value: bool) -> None:
"""Debug status
:param value: The debug status, True or False.
@@ -396,7 +511,7 @@ conf = petstore_api.Configuration(
httplib.HTTPConnection.debuglevel = 0
@property
def logger_format(self):
def logger_format(self) -> str:
"""The logger format.
The logger_formatter will be updated when sets logger_format.
@@ -407,7 +522,7 @@ conf = petstore_api.Configuration(
return self.__logger_format
@logger_format.setter
def logger_format(self, value):
def logger_format(self, value: str) -> None:
"""The logger format.
The logger_formatter will be updated when sets logger_format.
@@ -418,7 +533,7 @@ conf = petstore_api.Configuration(
self.__logger_format = value
self.logger_formatter = logging.Formatter(self.__logger_format)
def get_api_key_with_prefix(self, identifier, alias=None):
def get_api_key_with_prefix(self, identifier: str, alias: Optional[str]=None) -> Optional[str]:
"""Gets API key (with prefix if set).
:param identifier: The identifier of apiKey.
@@ -435,7 +550,9 @@ conf = petstore_api.Configuration(
else:
return key
def get_basic_auth_token(self):
return None
def get_basic_auth_token(self) -> Optional[str]:
"""Gets HTTP basic authentication header (string).
:return: The token for basic HTTP authentication.
@@ -450,12 +567,12 @@ conf = petstore_api.Configuration(
basic_auth=username + ':' + password
).get('authorization')
def auth_settings(self):
def auth_settings(self)-> AuthSettings:
"""Gets Auth Settings dict for api client.
:return: The Auth Settings information dict.
"""
auth = {}
auth: AuthSettings = {}
if self.access_token is not None:
auth['petstore_auth'] = {
'type': 'oauth2',
@@ -505,7 +622,7 @@ conf = petstore_api.Configuration(
}
return auth
def to_debug_report(self):
def to_debug_report(self) -> str:
"""Gets the essential information for debugging.
:return: The report for debugging.
@@ -517,7 +634,7 @@ conf = petstore_api.Configuration(
"SDK Package Version: 1.0.0".\
format(env=sys.platform, pyversion=sys.version)
def get_host_settings(self):
def get_host_settings(self) -> List[HostSetting]:
"""Gets an array of host settings
:return: An array of host settings
@@ -566,7 +683,12 @@ conf = petstore_api.Configuration(
}
]
def get_host_from_settings(self, index, variables=None, servers=None):
def get_host_from_settings(
self,
index: Optional[int],
variables: Optional[ServerVariablesT]=None,
servers: Optional[List[HostSetting]]=None,
) -> str:
"""Gets host URL based on the index and variables
:param index: array index of the host settings
:param variables: hash of variable and the corresponding value
@@ -606,12 +728,12 @@ conf = petstore_api.Configuration(
return url
@property
def host(self):
def host(self) -> str:
"""Return generated host."""
return self.get_host_from_settings(self.server_index, variables=self.server_variables)
@host.setter
def host(self, value):
def host(self, value: str) -> None:
"""Fix base path."""
self._base_path = value
self.server_index = None

View File

@@ -53,7 +53,7 @@ warn_unused_ignores = true
## Getting these passing should be easy
strict_equality = true
strict_concatenate = true
extra_checks = true
## Strongly recommend enabling this one as soon as you can
check_untyped_defs = true
@@ -74,3 +74,20 @@ disallow_any_generics = true
#
### This one can be tricky to get passing if you use a lot of untyped libraries
#warn_return_any = true
[[tool.mypy.overrides]]
module = [
"petstore_api.configuration",
]
warn_unused_ignores = true
strict_equality = true
extra_checks = 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