Skip to content

Commit 323d00f

Browse files
committed
chore(python): proper type hinting
1 parent ae08489 commit 323d00f

File tree

14 files changed

+103
-99
lines changed

14 files changed

+103
-99
lines changed

clients/algoliasearch-client-python/algoliasearch/http/api_response.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class ApiResponse(Generic[T]):
2121
def __init__(
2222
self,
2323
verb: Verb,
24-
data: T = None,
24+
data: Optional[T] = None,
2525
error_message: str = "",
2626
headers: Optional[Dict[str, str]] = None,
2727
host: str = "",

clients/algoliasearch-client-python/algoliasearch/http/base_config.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,6 @@
55

66

77
class BaseConfig:
8-
app_id: Optional[str]
9-
api_key: Optional[str]
10-
11-
read_timeout: int
12-
write_timeout: int
13-
connect_timeout: int
14-
15-
wait_task_time_before_retry: Optional[int]
16-
17-
headers: Dict[str, str]
18-
proxies: Dict[str, str]
19-
20-
hosts: HostsCollection
21-
228
def __init__(self, app_id: Optional[str] = None, api_key: Optional[str] = None):
239
app_id = environ.get("ALGOLIA_APP_ID") if app_id is None else app_id
2410

@@ -36,10 +22,10 @@ def __init__(self, app_id: Optional[str] = None, api_key: Optional[str] = None):
3622
self.write_timeout = 30000
3723
self.connect_timeout = 2000
3824

39-
self.wait_task_time_before_retry = None
40-
self.headers = None
41-
self.proxies = None
42-
self.hosts = None
25+
self.wait_task_time_before_retry: Optional[int] = None
26+
self.headers: Optional[Dict[str, str]] = None
27+
self.proxies: Optional[Dict[str, str]] = None
28+
self.hosts: Optional[HostsCollection] = None
4329

4430
def set_client_api_key(self, api_key: str) -> None:
4531
"""Sets a new API key to authenticate requests."""

clients/algoliasearch-client-python/algoliasearch/http/base_transporter.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
1-
from typing import Any, Dict, List
1+
from typing import Any, Dict
22

33
from algoliasearch.http.base_config import BaseConfig
4-
from algoliasearch.http.hosts import Host
4+
from algoliasearch.http.hosts import HostsCollection
55
from algoliasearch.http.request_options import RequestOptions
66
from algoliasearch.http.retry import RetryStrategy
77

88

99
class BaseTransporter:
10-
_config: BaseConfig
11-
_retry_strategy: RetryStrategy
12-
_hosts: List[Host]
13-
1410
def __init__(self, config: BaseConfig) -> None:
1511
self._config = config
1612
self._retry_strategy = RetryStrategy()
17-
self._hosts = []
13+
self._hosts: HostsCollection = []
14+
self._timeout = 5000
15+
16+
@property
17+
def config(self) -> BaseConfig:
18+
return self._config
1819

1920
def prepare(
2021
self,

clients/algoliasearch-client-python/algoliasearch/http/hosts.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ def __init__(
1818
self.port = port
1919
self.priority = cast(int, priority)
2020
self.accept = (CallType.WRITE | CallType.READ) if accept is None else accept
21-
22-
self.reset()
21+
self.last_use = 0.0
22+
self.retry_count = 0
23+
self.up = True
2324

2425
def reset(self) -> None:
2526
self.last_use = 0.0

clients/algoliasearch-client-python/algoliasearch/http/request_options.py

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from copy import deepcopy
22
from sys import version_info
3-
from typing import Any, Dict, List, Optional, Tuple, Union
3+
from typing import Any, Dict, Optional, Union
44
from urllib.parse import quote
55

66
from algoliasearch.http.base_config import BaseConfig
@@ -13,20 +13,22 @@
1313

1414

1515
class RequestOptions:
16-
_config: BaseConfig
17-
headers: Dict[str, str]
18-
query_parameters: Dict[str, Any]
19-
timeouts: Dict[str, int]
20-
data: Dict[str, Any]
21-
2216
def __init__(
2317
self,
2418
config: BaseConfig,
25-
headers: Dict[str, str] = {},
26-
query_parameters: Dict[str, Any] = {},
27-
timeouts: Dict[str, int] = {},
28-
data: Dict[str, Any] = {},
19+
headers: Dict[str, str] = None,
20+
query_parameters: Dict[str, Any] = None,
21+
timeouts: Dict[str, int] = None,
22+
data: Dict[str, Any] = None,
2923
) -> None:
24+
if headers is None:
25+
headers = {}
26+
if query_parameters is None:
27+
query_parameters = {}
28+
if timeouts is None:
29+
timeouts = {}
30+
if data is None:
31+
data = {}
3032
self._config = config
3133
self.headers = headers
3234
self.query_parameters = {
@@ -48,28 +50,39 @@ def from_dict(self, data: Dict[str, Dict[str, Any]]) -> Self:
4850
return RequestOptions(
4951
config=self._config,
5052
headers=data.get("headers", {}),
51-
query_parameters=data.get("query_parameters", {}),
53+
query_parameters={
54+
quote(k): quote(v)
55+
for k, v in QueryParametersSerializer(
56+
data.get("query_parameters", {})
57+
).query_parameters.items()
58+
},
5259
timeouts=data.get("timeouts", {}),
5360
data=data.get("data", {}),
5461
)
5562

5663
def merge(
5764
self,
58-
query_parameters: List[Tuple[str, str]] = [],
59-
headers: Dict[str, Optional[str]] = {},
60-
_: Dict[str, int] = {},
65+
query_parameters: Dict[str, Any] = None,
66+
headers: Dict[str, str] = None,
67+
timeouts: Dict[str, int] = None,
6168
data: Optional[str] = None,
6269
user_request_options: Optional[Union[Self, Dict[str, Any]]] = None,
6370
) -> Self:
6471
"""
6572
Merges the default config values with the user given request options if it exists.
6673
"""
6774

75+
if query_parameters is None:
76+
query_parameters = {}
77+
if headers is None:
78+
headers = {}
79+
if timeouts is None:
80+
timeouts = {}
6881
headers.update(self._config.headers)
6982

7083
request_options = {
7184
"headers": headers,
72-
"query_parameters": {k: v for k, v in query_parameters},
85+
"query_parameters": query_parameters,
7386
"timeouts": {
7487
"read": self._config.read_timeout,
7588
"write": self._config.write_timeout,

clients/algoliasearch-client-python/algoliasearch/http/retry.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class RetryOutcome:
1414
class RetryStrategy:
1515
def valid_hosts(self, hosts: List[Host]) -> List[Host]:
1616
for host in hosts:
17-
if not host.up and self._now() - host.last_use > Host.TTL:
17+
if not host.up and time.time() - host.last_use > Host.TTL:
1818
host.up = True
1919

2020
return [host for host in hosts if host.up]

clients/algoliasearch-client-python/algoliasearch/http/serializer.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,16 @@ class QueryParametersSerializer:
1010
Parses the given 'query_parameters' values of each keys into their string value.
1111
"""
1212

13-
query_parameters: Dict[str, Any] = {}
13+
def __init__(self, query_parameters: Optional[Dict[str, Any]]) -> None:
14+
self.query_parameters: Dict[str, Any] = {}
15+
if query_parameters is None:
16+
return
17+
for key, value in query_parameters.items():
18+
if isinstance(value, dict):
19+
for dkey, dvalue in value.items():
20+
self.query_parameters[dkey] = self.parse(dvalue)
21+
else:
22+
self.query_parameters[key] = self.parse(value)
1423

1524
def parse(self, value) -> Any:
1625
if isinstance(value, list):
@@ -27,19 +36,8 @@ def encoded(self) -> str:
2736
dict(sorted(self.query_parameters.items(), key=lambda val: val[0]))
2837
).replace("+", "%20")
2938

30-
def __init__(self, query_parameters: Optional[Dict[str, Any]]) -> None:
31-
self.query_parameters = {}
32-
if query_parameters is None:
33-
return
34-
for key, value in query_parameters.items():
35-
if isinstance(value, dict):
36-
for dkey, dvalue in value.items():
37-
self.query_parameters[dkey] = self.parse(dvalue)
38-
else:
39-
self.query_parameters[key] = self.parse(value)
40-
4139

42-
def bodySerializer(obj: Any) -> Any:
40+
def body_serializer(obj: Any) -> Any:
4341
"""Builds a JSON POST object.
4442
4543
If obj is None, return None.
@@ -57,14 +55,14 @@ def bodySerializer(obj: Any) -> Any:
5755
elif isinstance(obj, PRIMITIVE_TYPES):
5856
return obj
5957
elif isinstance(obj, list):
60-
return [bodySerializer(sub_obj) for sub_obj in obj]
58+
return [body_serializer(sub_obj) for sub_obj in obj]
6159
elif isinstance(obj, tuple):
62-
return tuple(bodySerializer(sub_obj) for sub_obj in obj)
60+
return tuple(body_serializer(sub_obj) for sub_obj in obj)
6361
elif isinstance(obj, dict):
6462
obj_dict = obj
6563
else:
6664
obj_dict = obj.to_dict()
6765
if obj_dict is None:
6866
return None
6967

70-
return {key: bodySerializer(val) for key, val in obj_dict.items()}
68+
return {key: body_serializer(val) for key, val in obj_dict.items()}

clients/algoliasearch-client-python/algoliasearch/http/transporter.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from asyncio import TimeoutError
22
from json import loads
3+
from typing import Optional
34

45
from aiohttp import ClientSession, TCPConnector
56
from async_timeout import timeout
@@ -11,19 +12,18 @@
1112
AlgoliaUnreachableHostException,
1213
RequestException,
1314
)
15+
from algoliasearch.http.hosts import HostsCollection
1416
from algoliasearch.http.request_options import RequestOptions
1517
from algoliasearch.http.retry import RetryOutcome, RetryStrategy
1618
from algoliasearch.http.verb import Verb
1719

1820

1921
class Transporter(BaseTransporter):
20-
_session: ClientSession
21-
2222
def __init__(self, config: BaseConfig) -> None:
23-
self._session = None
23+
self._session: Optional[ClientSession] = None
2424
self._config = config
2525
self._retry_strategy = RetryStrategy()
26-
self._hosts = []
26+
self._hosts: HostsCollection = []
2727

2828
async def close(self) -> None:
2929
if self._session is not None:
@@ -103,6 +103,7 @@ async def request(
103103

104104
class EchoTransporter(Transporter):
105105
def __init__(self, config: BaseConfig) -> None:
106+
super().__init__(config)
106107
self._config = config
107108
self._retry_strategy = RetryStrategy()
108109

clients/algoliasearch-client-python/algoliasearch/http/transporter_sync.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
from requests import Request, Session, Timeout
55

6+
from algoliasearch.http.hosts import HostsCollection
7+
68
if version_info >= (3, 11):
7-
from typing import Self
9+
from typing import Optional, Self
810
else:
911
from typing_extensions import Self
1012

@@ -24,13 +26,11 @@
2426

2527

2628
class TransporterSync(BaseTransporter):
27-
_session: Session
28-
2929
def __init__(self, config: BaseConfig) -> None:
30-
self._session = None
30+
self._session: Optional[Session] = None
3131
self._config = config
3232
self._retry_strategy = RetryStrategy()
33-
self._hosts = []
33+
self._hosts: HostsCollection = []
3434

3535
def __enter__(self) -> Self:
3636
return self
@@ -86,7 +86,7 @@ def request(
8686
url=url,
8787
host=host.url,
8888
status_code=resp.status_code,
89-
headers=resp.headers, # type: ignore -- insensitive dict is still a dict
89+
headers=resp.headers, # type: ignore # insensitive dict is still a dict
9090
data=resp.text,
9191
raw_data=resp.text,
9292
error_message=str(resp.reason),
@@ -117,6 +117,7 @@ def request(
117117

118118
class EchoTransporterSync(TransporterSync):
119119
def __init__(self, config: BaseConfig) -> None:
120+
super().__init__(config)
120121
self._config = config
121122
self._retry_strategy = RetryStrategy()
122123

clients/algoliasearch-client-python/algoliasearch/http/user_agent.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@
1111

1212

1313
class UserAgent:
14+
def __init__(self) -> None:
15+
self.value = "Algolia for Python ({}); Python ({})".format(
16+
__version__, str(python_version())
17+
)
18+
1419
def get(self) -> str:
1520
return self.value
1621

1722
def add(self, segment: str, version: Optional[str] = __version__) -> Self:
1823
self.value += "; {} ({})".format(segment, version)
1924
return self
20-
21-
def __init__(self) -> None:
22-
self.value = "Algolia for Python ({}); Python ({})".format(
23-
__version__, str(python_version())
24-
)

templates/python/api.mustache

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
4141

4242
def __init__(self, app_id: Optional[str] = None, api_key: Optional[str] = None, {{#hasRegionalHost}}region: {{#fallbackToAliasHost}}Optional[str]{{/fallbackToAliasHost}}{{^fallbackToAliasHost}}str{{/fallbackToAliasHost}} = None, {{/hasRegionalHost}}transporter: Optional[Transporter{{#isSyncClient}}Sync{{/isSyncClient}}] = None, config: Optional[{{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}] = None) -> None:
4343
if transporter is not None and config is None:
44-
config = transporter._config
44+
config = transporter.config
4545

4646
if config is None:
4747
config = {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}(app_id, api_key{{#hasRegionalHost}}, region{{/hasRegionalHost}})
@@ -99,7 +99,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
9999

100100
{{^isSyncClient}}async {{/isSyncClient}}def set_client_api_key(self, api_key: str) -> None:
101101
"""Sets a new API key to authenticate requests."""
102-
self._transporter._config.set_client_api_key(api_key)
102+
self._transporter.config.set_client_api_key(api_key)
103103

104104
{{#isSearchClient}}
105105
{{> search_helpers}}
@@ -138,7 +138,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
138138
{{/allParams}}
139139

140140
{{#queryParams.0}}
141-
_query_parameters: List[Tuple[str, str]] = []
141+
_query_parameters: Dict[str, Any] = {}
142142
{{/queryParams.0}}
143143
{{#headerParams.0}}
144144
_headers: Dict[str, Optional[str]] = {}
@@ -148,11 +148,11 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
148148
{{#queryParams}}
149149
if {{paramName}} is not None:
150150
{{^x-is-custom-request}}
151-
_query_parameters.append(('{{baseName}}', {{paramName}}))
151+
_query_parameters["{{baseName}}"] = {{paramName}}
152152
{{/x-is-custom-request}}
153153
{{#x-is-custom-request}}
154154
for _qpkey, _qpvalue in {{paramName}}.items():
155-
_query_parameters.append((_qpkey, _qpvalue))
155+
_query_parameters[_qpkey] = _qpvalue
156156
{{/x-is-custom-request}}
157157
{{/queryParams}}
158158
{{/vendorExtensions}}
@@ -174,7 +174,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
174174
request_options=self._request_options.merge(
175175
{{#queryParams.0}}query_parameters=_query_parameters,{{/queryParams.0}}
176176
{{#headerParams.0}}headers=_headers,{{/headerParams.0}}
177-
{{#bodyParam}}data=dumps(bodySerializer(_data)),{{/bodyParam}}
177+
{{#bodyParam}}data=dumps(body_serializer(_data)),{{/bodyParam}}
178178
user_request_options=request_options,
179179
),
180180
{{#vendorExtensions}}

templates/python/config.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ class {{#lambda.pascalcase}}{{client}}{{/lambda.pascalcase}}Config(BaseConfig):
4040
if {{^fallbackToAliasHost}}not region or {{/fallbackToAliasHost}}(region is not None and region not in _regions):
4141
raise ValueError(f"`region` {{^fallbackToAliasHost}}is required and {{/fallbackToAliasHost}}must be one of the following: {', '.join(_regions)}")
4242

43-
self.hosts = HostsCollection([Host({{#fallbackToAliasHost}}"{{{hostWithFallback}}}" if region is None else {{/fallbackToAliasHost}} "{{{regionalHost}}}".replace("{region}", region))])
43+
self.hosts = HostsCollection([Host({{#fallbackToAliasHost}}"{{{hostWithFallback}}}" if region is None else {{/fallbackToAliasHost}} "{{{regionalHost}}}".replace("{region}", region or ""))])
4444
{{/hasRegionalHost}}
4545

4646
{{^hasRegionalHost}}

0 commit comments

Comments
 (0)