Skip to content

Commit 57cfff1

Browse files
authored
python: stronger typing for the "configuration" module (#20014)
* python: strong typing for the configuration module * 3.8 compatibility * fix bearer format * Specific auth settings
1 parent 54920ff commit 57cfff1

File tree

11 files changed

+1268
-525
lines changed

11 files changed

+1268
-525
lines changed

modules/openapi-generator/src/main/resources/python/configuration.mustache

Lines changed: 179 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,152 @@
33
{{>partial_header}}
44

55
import copy
6+
import http.client as httplib
67
import logging
78
from logging import FileHandler
89
{{^asyncio}}
910
import multiprocessing
1011
{{/asyncio}}
1112
import sys
12-
from typing import Optional
13+
from typing import Any, ClassVar, Dict, List, Literal, Optional, TypedDict
14+
from typing_extensions import NotRequired, Self
15+
1316
import urllib3
1417

15-
import http.client as httplib
18+
{{#hasHttpSignatureMethods}}
19+
from {{packageName}}.signing import HttpSigningConfiguration
20+
{{/hasHttpSignatureMethods}}
1621

1722
JSON_SCHEMA_VALIDATION_KEYWORDS = {
1823
'multipleOf', 'maximum', 'exclusiveMaximum',
1924
'minimum', 'exclusiveMinimum', 'maxLength',
2025
'minLength', 'pattern', 'maxItems', 'minItems'
2126
}
2227

28+
ServerVariablesT = Dict[str, str]
29+
30+
GenericAuthSetting = TypedDict(
31+
"GenericAuthSetting",
32+
{
33+
"type": str,
34+
"in": str,
35+
"key": str,
36+
"value": str,
37+
},
38+
)
39+
40+
41+
OAuth2AuthSetting = TypedDict(
42+
"OAuth2AuthSetting",
43+
{
44+
"type": Literal["oauth2"],
45+
"in": Literal["header"],
46+
"key": Literal["Authorization"],
47+
"value": str,
48+
},
49+
)
50+
51+
52+
APIKeyAuthSetting = TypedDict(
53+
"APIKeyAuthSetting",
54+
{
55+
"type": Literal["api_key"],
56+
"in": str,
57+
"key": str,
58+
"value": Optional[str],
59+
},
60+
)
61+
62+
63+
BasicAuthSetting = TypedDict(
64+
"BasicAuthSetting",
65+
{
66+
"type": Literal["basic"],
67+
"in": Literal["header"],
68+
"key": Literal["Authorization"],
69+
"value": Optional[str],
70+
},
71+
)
72+
73+
74+
BearerFormatAuthSetting = TypedDict(
75+
"BearerFormatAuthSetting",
76+
{
77+
"type": Literal["bearer"],
78+
"in": Literal["header"],
79+
"format": Literal["JWT"],
80+
"key": Literal["Authorization"],
81+
"value": str,
82+
},
83+
)
84+
85+
86+
BearerAuthSetting = TypedDict(
87+
"BearerAuthSetting",
88+
{
89+
"type": Literal["bearer"],
90+
"in": Literal["header"],
91+
"key": Literal["Authorization"],
92+
"value": str,
93+
},
94+
)
95+
96+
97+
HTTPSignatureAuthSetting = TypedDict(
98+
"HTTPSignatureAuthSetting",
99+
{
100+
"type": Literal["http-signature"],
101+
"in": Literal["header"],
102+
"key": Literal["Authorization"],
103+
"value": None,
104+
},
105+
)
106+
107+
108+
AuthSettings = TypedDict(
109+
"AuthSettings",
110+
{
111+
{{#authMethods}}
112+
{{#isOAuth}}
113+
"{{name}}": OAuth2AuthSetting,
114+
{{/isOAuth}}
115+
{{#isApiKey}}
116+
"{{name}}": APIKeyAuthSetting,
117+
{{/isApiKey}}
118+
{{#isBasic}}
119+
{{#isBasicBasic}}
120+
"{{name}}": BasicAuthSetting,
121+
{{/isBasicBasic}}
122+
{{#isBasicBearer}}
123+
{{#bearerFormat}}
124+
"{{name}}": BearerFormatAuthSetting,
125+
{{/bearerFormat}}
126+
{{^bearerFormat}}
127+
"{{name}}": BearerAuthSetting,
128+
{{/bearerFormat}}
129+
{{/isBasicBearer}}
130+
{{#isHttpSignature}}
131+
"{{name}}": HTTPSignatureAuthSetting,
132+
{{/isHttpSignature}}
133+
{{/isBasic}}
134+
{{/authMethods}}
135+
},
136+
total=False,
137+
)
138+
139+
140+
class HostSettingVariable(TypedDict):
141+
description: str
142+
default_value: str
143+
enum_values: List[str]
144+
145+
146+
class HostSetting(TypedDict):
147+
url: str
148+
description: str
149+
variables: NotRequired[Dict[str, HostSettingVariable]]
150+
151+
23152
class Configuration:
24153
"""This class contains various settings of the API client.
25154

@@ -140,23 +269,29 @@ conf = {{{packageName}}}.Configuration(
140269
{{/hasAuthMethods}}
141270
"""
142271

143-
_default = None
272+
_default: ClassVar[Optional[Self]] = None
144273

145-
def __init__(self, host=None,
146-
api_key=None, api_key_prefix=None,
147-
username=None, password=None,
148-
access_token=None,
274+
def __init__(
275+
self,
276+
host: Optional[str]=None,
277+
api_key: Optional[Dict[str, str]]=None,
278+
api_key_prefix: Optional[Dict[str, str]]=None,
279+
username: Optional[str]=None,
280+
password: Optional[str]=None,
281+
access_token: Optional[str]=None,
149282
{{#hasHttpSignatureMethods}}
150-
signing_info=None,
283+
signing_info: Optional[HttpSigningConfiguration]=None,
151284
{{/hasHttpSignatureMethods}}
152-
server_index=None, server_variables=None,
153-
server_operation_index=None, server_operation_variables=None,
154-
ignore_operation_servers=False,
155-
ssl_ca_cert=None,
156-
retries=None,
157-
*,
158-
debug: Optional[bool] = None
159-
) -> None:
285+
server_index: Optional[int]=None,
286+
server_variables: Optional[ServerVariablesT]=None,
287+
server_operation_index: Optional[Dict[int, int]]=None,
288+
server_operation_variables: Optional[Dict[int, ServerVariablesT]]=None,
289+
ignore_operation_servers: bool=False,
290+
ssl_ca_cert: Optional[str]=None,
291+
retries: Optional[int] = None,
292+
*,
293+
debug: Optional[bool] = None,
294+
) -> None:
160295
"""Constructor
161296
"""
162297
self._base_path = "{{{basePath}}}" if host is None else host
@@ -295,7 +430,7 @@ conf = {{{packageName}}}.Configuration(
295430
"""date format
296431
"""
297432

298-
def __deepcopy__(self, memo):
433+
def __deepcopy__(self, memo: Dict[int, Any]) -> Self:
299434
cls = self.__class__
300435
result = cls.__new__(cls)
301436
memo[id(self)] = result
@@ -309,7 +444,7 @@ conf = {{{packageName}}}.Configuration(
309444
result.debug = self.debug
310445
return result
311446

312-
def __setattr__(self, name, value):
447+
def __setattr__(self, name: str, value: Any) -> None:
313448
object.__setattr__(self, name, value)
314449
{{#hasHttpSignatureMethods}}
315450
if name == "signing_info" and value is not None:
@@ -319,7 +454,7 @@ conf = {{{packageName}}}.Configuration(
319454
{{/hasHttpSignatureMethods}}
320455

321456
@classmethod
322-
def set_default(cls, default):
457+
def set_default(cls, default: Optional[Self]) -> None:
323458
"""Set default instance of configuration.
324459

325460
It stores default configuration, which can be
@@ -330,7 +465,7 @@ conf = {{{packageName}}}.Configuration(
330465
cls._default = default
331466

332467
@classmethod
333-
def get_default_copy(cls):
468+
def get_default_copy(cls) -> Self:
334469
"""Deprecated. Please use `get_default` instead.
335470

336471
Deprecated. Please use `get_default` instead.
@@ -340,7 +475,7 @@ conf = {{{packageName}}}.Configuration(
340475
return cls.get_default()
341476

342477
@classmethod
343-
def get_default(cls):
478+
def get_default(cls) -> Self:
344479
"""Return the default configuration.
345480

346481
This method returns newly created, based on default constructor,
@@ -350,11 +485,11 @@ conf = {{{packageName}}}.Configuration(
350485
:return: The configuration object.
351486
"""
352487
if cls._default is None:
353-
cls._default = Configuration()
488+
cls._default = cls()
354489
return cls._default
355490

356491
@property
357-
def logger_file(self):
492+
def logger_file(self) -> Optional[str]:
358493
"""The logger file.
359494

360495
If the logger_file is None, then add stream handler and remove file
@@ -366,7 +501,7 @@ conf = {{{packageName}}}.Configuration(
366501
return self.__logger_file
367502

368503
@logger_file.setter
369-
def logger_file(self, value):
504+
def logger_file(self, value: Optional[str]) -> None:
370505
"""The logger file.
371506

372507
If the logger_file is None, then add stream handler and remove file
@@ -385,7 +520,7 @@ conf = {{{packageName}}}.Configuration(
385520
logger.addHandler(self.logger_file_handler)
386521

387522
@property
388-
def debug(self):
523+
def debug(self) -> bool:
389524
"""Debug status
390525

391526
:param value: The debug status, True or False.
@@ -394,7 +529,7 @@ conf = {{{packageName}}}.Configuration(
394529
return self.__debug
395530

396531
@debug.setter
397-
def debug(self, value):
532+
def debug(self, value: bool) -> None:
398533
"""Debug status
399534

400535
:param value: The debug status, True or False.
@@ -416,7 +551,7 @@ conf = {{{packageName}}}.Configuration(
416551
httplib.HTTPConnection.debuglevel = 0
417552

418553
@property
419-
def logger_format(self):
554+
def logger_format(self) -> str:
420555
"""The logger format.
421556

422557
The logger_formatter will be updated when sets logger_format.
@@ -427,7 +562,7 @@ conf = {{{packageName}}}.Configuration(
427562
return self.__logger_format
428563

429564
@logger_format.setter
430-
def logger_format(self, value):
565+
def logger_format(self, value: str) -> None:
431566
"""The logger format.
432567

433568
The logger_formatter will be updated when sets logger_format.
@@ -438,7 +573,7 @@ conf = {{{packageName}}}.Configuration(
438573
self.__logger_format = value
439574
self.logger_formatter = logging.Formatter(self.__logger_format)
440575

441-
def get_api_key_with_prefix(self, identifier, alias=None):
576+
def get_api_key_with_prefix(self, identifier: str, alias: Optional[str]=None) -> Optional[str]:
442577
"""Gets API key (with prefix if set).
443578

444579
:param identifier: The identifier of apiKey.
@@ -455,7 +590,9 @@ conf = {{{packageName}}}.Configuration(
455590
else:
456591
return key
457592

458-
def get_basic_auth_token(self):
593+
return None
594+
595+
def get_basic_auth_token(self) -> Optional[str]:
459596
"""Gets HTTP basic authentication header (string).
460597

461598
:return: The token for basic HTTP authentication.
@@ -470,12 +607,12 @@ conf = {{{packageName}}}.Configuration(
470607
basic_auth=username + ':' + password
471608
).get('authorization')
472609

473-
def auth_settings(self):
610+
def auth_settings(self)-> AuthSettings:
474611
"""Gets Auth Settings dict for api client.
475612

476613
:return: The Auth Settings information dict.
477614
"""
478-
auth = {}
615+
auth: AuthSettings = {}
479616
{{#authMethods}}
480617
{{#isApiKey}}
481618
if '{{name}}' in self.api_key{{#vendorExtensions.x-auth-id-alias}} or '{{.}}' in self.api_key{{/vendorExtensions.x-auth-id-alias}}:
@@ -533,7 +670,7 @@ conf = {{{packageName}}}.Configuration(
533670
{{/authMethods}}
534671
return auth
535672

536-
def to_debug_report(self):
673+
def to_debug_report(self) -> str:
537674
"""Gets the essential information for debugging.
538675

539676
:return: The report for debugging.
@@ -545,7 +682,7 @@ conf = {{{packageName}}}.Configuration(
545682
"SDK Package Version: {{packageVersion}}".\
546683
format(env=sys.platform, pyversion=sys.version)
547684

548-
def get_host_settings(self):
685+
def get_host_settings(self) -> List[HostSetting]:
549686
"""Gets an array of host settings
550687

551688
:return: An array of host settings
@@ -580,7 +717,12 @@ conf = {{{packageName}}}.Configuration(
580717
{{/servers}}
581718
]
582719

583-
def get_host_from_settings(self, index, variables=None, servers=None):
720+
def get_host_from_settings(
721+
self,
722+
index: Optional[int],
723+
variables: Optional[ServerVariablesT]=None,
724+
servers: Optional[List[HostSetting]]=None,
725+
) -> str:
584726
"""Gets host URL based on the index and variables
585727
:param index: array index of the host settings
586728
:param variables: hash of variable and the corresponding value
@@ -620,12 +762,12 @@ conf = {{{packageName}}}.Configuration(
620762
return url
621763

622764
@property
623-
def host(self):
765+
def host(self) -> str:
624766
"""Return generated host."""
625767
return self.get_host_from_settings(self.server_index, variables=self.server_variables)
626768

627769
@host.setter
628-
def host(self, value):
770+
def host(self, value: str) -> None:
629771
"""Fix base path."""
630772
self._base_path = value
631773
self.server_index = None

0 commit comments

Comments
 (0)