Skip to content

fix(python): missing transporter close #3741

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@

T = TypeVar("T")

PRIMITIVE_TYPES = (float, bool, bytes, str, int)


class ApiResponse(Generic[T]):
"""
API response object
"""

PRIMITIVE_TYPES = (float, bool, bytes, str, int)

def __init__(
self,
verb: Verb,
Expand All @@ -29,8 +29,8 @@ def __init__(
is_timed_out_error: bool = False,
path: str = "",
query_parameters: Optional[Dict[str, Any]] = None,
raw_data: str = None,
status_code: int = None,
raw_data: Optional[str] = None,
status_code: Optional[int] = None,
timeouts: Optional[Dict[str, int]] = None,
url: str = "",
) -> None:
Expand All @@ -51,34 +51,37 @@ def __init__(
def to_json(self) -> str:
return str(self.__dict__)

def deserialize(self, klass: any = None, data: any = None) -> T:
@staticmethod
def deserialize(klass: Any = None, data: Any = None) -> Any:
"""Deserializes dict, list, str into an object.

:param data: dict, list or str.
:param klass: class literal, or string of class name.

:return: object.
"""
if data is None:
data = self.raw_data
if data is None:
return None

if hasattr(klass, "__origin__") and klass.__origin__ is list:
sub_kls = klass.__args__[0]
arr = json.loads(data)
return [self.deserialize(sub_kls, sub_data) for sub_data in arr]
return [ApiResponse.deserialize(sub_kls, sub_data) for sub_data in arr]

if isinstance(klass, str):
if klass.startswith("List["):
sub_kls = match(r"List\[(.*)]", klass).group(1)
return [self.deserialize(sub_kls, sub_data) for sub_data in data]
sub_kls = match(r"List\[(.*)]", klass)
if sub_kls is not None:
sub_kls = sub_kls.group(1)
return [ApiResponse.deserialize(sub_kls, sub_data) for sub_data in data]

if klass.startswith("Dict["):
sub_kls = match(r"Dict\[([^,]*), (.*)]", klass).group(2)
return {k: self.deserialize(sub_kls, v) for k, v in data.items()}
sub_kls = match(r"Dict\[([^,]*), (.*)]", klass)
if sub_kls is not None:
sub_kls = sub_kls.group(2)
return {k: ApiResponse.deserialize(sub_kls, v) for k, v in data.items()}

if klass in self.PRIMITIVE_TYPES:
if klass in PRIMITIVE_TYPES:
try:
return klass(data)
except UnicodeEncodeError:
Expand Down
24 changes: 12 additions & 12 deletions clients/algoliasearch-client-python/algoliasearch/http/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,37 @@

import asyncio
import time
from typing import Callable, TypeVar
from typing import Awaitable, Callable, Optional, TypeVar

T = TypeVar("T")


class Timeout:
def __call__(self) -> int:
return 0
def __call__(self, retry_count: int = 0) -> int:
return retry_count

def __init__(self) -> None:
pass


class RetryTimeout(Timeout):
def __call__(self, retry_count: int) -> int:
def __call__(self, retry_count: int = 0) -> int:
return int(min(retry_count * 0.2, 5))


async def create_iterable(
func: Callable[[T], T],
func: Callable[[Optional[T]], Awaitable[T]],
validate: Callable[[T], bool],
aggregator: Callable[[T], None],
timeout: Timeout = Timeout(),
error_validate: Callable[[T], bool] = None,
error_message: Callable[[T], str] = None,
error_validate: Optional[Callable[[T], bool]] = None,
error_message: Optional[Callable[[T], str]] = None,
) -> T:
"""
Helper: Iterates until the given `func` until `timeout` or `validate`.
"""

async def retry(prev: T = None) -> T:
async def retry(prev: Optional[T] = None) -> T:
resp = await func(prev)

if aggregator:
Expand All @@ -53,18 +53,18 @@ async def retry(prev: T = None) -> T:


def create_iterable_sync(
func: Callable[[T], T],
func: Callable[[Optional[T]], T],
validate: Callable[[T], bool],
aggregator: Callable[[T], None],
timeout: Timeout = Timeout(),
error_validate: Callable[[T], bool] = None,
error_message: Callable[[T], str] = None,
error_validate: Optional[Callable[[T], bool]] = None,
error_message: Optional[Callable[[T], str]] = None,
) -> T:
"""
Helper: Iterates until the given `func` until `timeout` or `validate`.
"""

def retry(prev: T = None) -> T:
def retry(prev: Optional[T] = None) -> T:
resp = func(prev)

if aggregator:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def to_dict(self) -> Dict[str, Any]:
def to_json(self) -> str:
return str(self.__dict__)

def from_dict(self, data: Optional[Dict[str, Dict[str, Any]]]) -> Self:
def from_dict(self, data: Dict[str, Dict[str, Any]]) -> Self:
return RequestOptions(
config=self._config,
headers=data.get("headers", {}),
Expand All @@ -57,8 +57,8 @@ def merge(
self,
query_parameters: List[Tuple[str, str]] = [],
headers: Dict[str, Optional[str]] = {},
timeouts: Dict[str, int] = {},
data: Optional[Union[dict, list]] = None,
_: Dict[str, int] = {},
data: Optional[str] = None,
user_request_options: Optional[Union[Self, Dict[str, Any]]] = None,
) -> Self:
"""
Expand All @@ -85,9 +85,6 @@ def merge(
_user_request_options = user_request_options

for key, value in _user_request_options.items():
if key == "data" and isinstance(value, dict):
request_options.data = value
continue
request_options[key].update(value)

return self.from_dict(request_options)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from json import dumps
from typing import Any, Dict
from typing import Any, Dict, Optional
from urllib.parse import urlencode

PRIMITIVE_TYPES = (float, bool, bytes, str, int)
Expand Down Expand Up @@ -27,7 +27,7 @@ def encoded(self) -> str:
dict(sorted(self.query_parameters.items(), key=lambda val: val[0]))
).replace("+", "%20")

def __init__(self, query_parameters: Dict[str, Any]) -> None:
def __init__(self, query_parameters: Optional[Dict[str, Any]]) -> None:
self.query_parameters = {}
if query_parameters is None:
return
Expand All @@ -39,7 +39,7 @@ def __init__(self, query_parameters: Dict[str, Any]) -> None:
self.query_parameters[key] = self.parse(value)


def bodySerializer(obj: Any) -> dict:
def bodySerializer(obj: Any) -> Any:
"""Builds a JSON POST object.

If obj is None, return None.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ def __init__(self, config: BaseConfig) -> None:
self._retry_strategy = RetryStrategy()
self._hosts = []

async def close(self) -> None:
if self._session is not None:
_session = self._session
self._session = None

await _session.close()

async def request(
self,
verb: Verb,
Expand Down
8 changes: 5 additions & 3 deletions templates/python/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
transporter = Transporter{{#isSyncClient}}Sync{{/isSyncClient}}(config)
self._transporter = transporter

def create_with_config(config: {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}, transporter: Optional[Transporter{{#isSyncClient}}Sync{{/isSyncClient}}] = None) -> Self:
@classmethod
def create_with_config(cls, config: {{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}, transporter: Optional[Transporter{{#isSyncClient}}Sync{{/isSyncClient}}] = None) -> {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
"""Allows creating a client with a customized `{{#lambda.pascalcase}}{{client}}Config{{/lambda.pascalcase}}` and `Transporter{{#isSyncClient}}Sync{{/isSyncClient}}`. If `transporter` is not provided, the default one will be initialized from the given `config`.

Args:
Expand All @@ -72,7 +73,7 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
return {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}(app_id=config.app_id, api_key=config.api_key, {{#hasRegionalHost}}region=config.region, {{/hasRegionalHost}}transporter=transporter, config=config)

{{^isSyncClient}}
async def __aenter__(self) -> None:
async def __aenter__(self) -> Self:
return self

async def __aexit__(self, exc_type, exc_value, traceback) -> None:
Expand Down Expand Up @@ -202,7 +203,8 @@ class {{classname}}{{#isSyncClient}}Sync{{/isSyncClient}}:
:return: Returns the deserialized response in a '{{{returnType}}}' result object.
{{/returnType}}
"""
return ({{^isSyncClient}}await {{/isSyncClient}}self.{{operationId}}_with_http_info({{#allParams}}{{paramName}},{{/allParams}}request_options)).deserialize({{{returnType}}})
resp = {{^isSyncClient}}await {{/isSyncClient}}self.{{operationId}}_with_http_info({{#allParams}}{{paramName}},{{/allParams}}request_options)
return resp.deserialize({{#returnType}}{{{.}}}{{/returnType}}{{^returnType}}None{{/returnType}}, resp.raw_data)

{{/operation}}
{{/operations}}
Expand Down
2 changes: 1 addition & 1 deletion templates/python/pyproject.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ classifiers = [
]

[tool.poetry.dependencies]
python = "^3.8.1"
python = ">= 3.8.1"
urllib3 = ">= 1.25.3"
aiohttp = ">= 3.9.2"
requests = ">=2.32.3"
Expand Down
4 changes: 2 additions & 2 deletions templates/python/search_helpers.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
aggregator=_aggregator,
validate=lambda _resp: _resp.status == "published",
timeout=lambda: timeout(self._retry_count),
error_validate=lambda x: self._retry_count >= max_retries,
error_validate=lambda _: self._retry_count >= max_retries,
error_message=lambda: f"The maximum number of retries exceeded. (${self._retry_count}/${max_retries})",
)

Expand All @@ -49,7 +49,7 @@
aggregator=_aggregator,
validate=lambda _resp: _resp.status == "published",
timeout=lambda: timeout(self._retry_count),
error_validate=lambda x: self._retry_count >= max_retries,
error_validate=lambda _: self._retry_count >= max_retries,
error_message=lambda: f"The maximum number of retries exceeded. (${self._retry_count}/${max_retries})",
)

Expand Down
Loading