Skip to content

Commit

Permalink
Merge pull request #1165 from stripe/richardm-async-wip
Browse files Browse the repository at this point in the history
Beta: raw_request_async with HTTPX
  • Loading branch information
richardm-stripe authored Jan 4, 2024
2 parents f26b002 + f96c67b commit be61235
Show file tree
Hide file tree
Showing 13 changed files with 999 additions and 46 deletions.
4 changes: 4 additions & 0 deletions stripe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
verify_ssl_certs: bool = True
proxy: Optional[str] = None
default_http_client: Optional["HTTPClient"] = None
default_http_client_async: Optional["HTTPClientAsync"] = None
app_info: Optional[AppInfo] = None
enable_telemetry: bool = True
max_network_retries: int = 0
Expand All @@ -48,6 +49,7 @@
WebhookSignature as WebhookSignature,
)
from stripe._raw_request import raw_request as raw_request # noqa
from stripe._raw_request import raw_request_async as raw_request_async # noqa
from stripe._raw_request import deserialize as deserialize # noqa

from stripe._preview import preview as preview # noqa
Expand Down Expand Up @@ -142,9 +144,11 @@ def set_app_info(
# HttpClient
from stripe._http_client import (
HTTPClient as HTTPClient,
HTTPClientAsync as HTTPClientAsync,
PycurlClient as PycurlClient,
RequestsClient as RequestsClient,
UrlFetchClient as UrlFetchClient,
HTTPXClient as HTTPXClient,
new_default_http_client as new_default_http_client,
)

Expand Down
132 changes: 122 additions & 10 deletions stripe/_api_requestor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
Optional,
Tuple,
cast,
Union,
)
from typing_extensions import NoReturn, Literal
import uuid
Expand Down Expand Up @@ -78,6 +79,14 @@ def __init__(
self._client = stripe.default_http_client
self._default_proxy = proxy

if not stripe.default_http_client_async:
stripe.default_http_client_async = (
_http_client.new_default_http_client_async(
verify_ssl_certs=verify, proxy=proxy
)
)
self._client_async = stripe.default_http_client_async

@classmethod
@_util.deprecated(
"This method is internal to stripe-python and the public interface will be removed in a future stripe-python version"
Expand Down Expand Up @@ -116,6 +125,28 @@ def request(
resp = self.interpret_response(rbody, rcode, rheaders)
return resp, my_api_key

async def request_async(
self,
method: str,
url: str,
params: Optional[Mapping[str, Any]] = None,
headers: Optional[Mapping[str, str]] = None,
api_mode: Optional[Literal["preview", "standard"]] = None,
*,
_usage: Optional[List[str]] = None,
) -> Tuple[StripeResponse, str]:
rbody, rcode, rheaders, my_api_key = await self.request_raw_async(
method.lower(),
url,
params,
headers,
is_streaming=False,
api_mode=api_mode,
_usage=_usage,
)
resp = self.interpret_response(rbody, rcode, rheaders)
return resp, my_api_key

def request_stream(
self,
method: str,
Expand Down Expand Up @@ -144,6 +175,31 @@ def request_stream(
)
return resp, my_api_key

async def request_stream_async(
self,
method: str,
url: str,
params: Optional[Mapping[str, Any]] = None,
headers: Optional[Mapping[str, str]] = None,
api_mode: Optional[Literal["preview", "standard"]] = None,
) -> Tuple[StripeStreamResponse, str]:
stream, rcode, rheaders, my_api_key = await self.request_raw_async(
method.lower(),
url,
params,
headers,
is_streaming=True,
api_mode=api_mode,
)
resp = self.interpret_streaming_response(
# TODO: should be able to remove this cast once self._client.request_stream_with_retries
# returns a more specific type.
cast(IOBase, stream),
rcode,
rheaders,
)
return resp, my_api_key

def handle_error_response(self, rbody, rcode, resp, rheaders) -> NoReturn:
try:
error_data = resp["error"]
Expand Down Expand Up @@ -319,7 +375,7 @@ def request_headers(self, api_key, method, api_mode):

return headers

def request_raw(
def _get_request_raw_args(
self,
method: str,
url: str,
Expand All @@ -329,7 +385,7 @@ def request_raw(
api_mode: Optional[Literal["preview", "standard"]] = None,
*,
_usage: Optional[List[str]] = None,
) -> Tuple[object, int, Mapping[str, str], str]:
) -> Tuple[str, Dict[str, str], Optional[Union[bytes, str]], str]:
"""
Mechanism for issuing an API call
"""
Expand Down Expand Up @@ -407,6 +463,37 @@ def request_raw(
api_version=self.api_version,
)

return abs_url, headers, post_data, my_api_key

@staticmethod
def _log_response(abs_url, rcode, rcontent, rheaders):
_util.log_info(
"Stripe API response", path=abs_url, response_code=rcode
)
_util.log_debug("API response body", body=rcontent)

if "Request-Id" in rheaders:
request_id = rheaders["Request-Id"]
_util.log_debug(
"Dashboard link for request",
link=_util.dashboard_link(request_id),
)

def request_raw(
self,
method: str,
url: str,
params: Optional[Mapping[str, Any]] = None,
supplied_headers: Optional[Mapping[str, str]] = None,
is_streaming: bool = False,
api_mode: Optional[Literal["preview", "standard"]] = None,
*,
_usage: Optional[List[str]] = None,
) -> Tuple[object, int, Mapping[str, str], str]:
abs_url, headers, post_data, my_api_key = self._get_request_raw_args(
method, url, params, supplied_headers, is_streaming, api_mode
)

if is_streaming:
(
rcontent,
Expand All @@ -420,18 +507,43 @@ def request_raw(
method, abs_url, headers, post_data, _usage=_usage
)

_util.log_info(
"Stripe API response", path=abs_url, response_code=rcode
self._log_response(abs_url, rcode, rcontent, rheaders)

return rcontent, rcode, rheaders, my_api_key

async def request_raw_async(
self,
method: str,
url: str,
params: Optional[Mapping[str, Any]] = None,
supplied_headers: Optional[Mapping[str, str]] = None,
is_streaming: bool = False,
api_mode: Optional[Literal["preview", "standard"]] = None,
*,
_usage: Optional[List[str]] = None,
) -> Tuple[object, int, Mapping[str, str], str]:
abs_url, headers, post_data, my_api_key = self._get_request_raw_args(
method, url, params, supplied_headers, is_streaming, api_mode
)
_util.log_debug("API response body", body=rcontent)

if "Request-Id" in rheaders:
request_id = rheaders["Request-Id"]
_util.log_debug(
"Dashboard link for request",
link=_util.dashboard_link(request_id),
if is_streaming:
(
rcontent,
rcode,
rheaders,
) = await self._client_async.request_stream_with_retries_async(
method, abs_url, headers, post_data, _usage=_usage
)
else:
(
rcontent,
rcode,
rheaders,
) = await self._client_async.request_with_retries_async(
method, abs_url, headers, post_data, _usage=_usage
)

self._log_response(abs_url, rcode, rcontent, rheaders)
return rcontent, rcode, rheaders, my_api_key

def _should_handle_code_as_error(self, rcode):
Expand Down
Loading

0 comments on commit be61235

Please sign in to comment.