-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
[core] add provisional azure.core.rest #19502
Changes from all commits
6dff5d5
58c184a
c7663fe
bec31ed
405d5e8
fb692df
f826377
6ceb795
0f10e5e
c35fdc2
f8fd247
c4fd6d2
b322e28
449f42a
a021de2
7a68dd3
df42f15
da62f4b
e06ebb4
04ddbd0
e860e8a
2f39ad2
f503b2c
8ccce34
30c6e39
20e593a
1483f8c
4cbf6a8
aabe409
cf637ef
0e596ce
c930e74
d20fa00
883c761
e35d198
b26a9e7
f6ba2a9
37ae392
f23a9b9
28adf60
6ffe5db
10a2350
8deb6d7
680f423
6b23aa2
7793a2a
051743c
911230c
a43004c
b392f7c
61edc06
038f8da
49e3c33
c6860f2
831a439
8b3c812
f9ecfdb
16ed67b
934f53d
fcbd91d
2667239
5be9b52
930509a
7fd947f
931aba3
27f4af9
9d4013b
59f0015
a575549
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,7 @@ | |
|
||
import logging | ||
from collections.abc import Iterable | ||
from typing import Any, Awaitable | ||
from .configuration import Configuration | ||
from .pipeline import AsyncPipeline | ||
from .pipeline.transport._base import PipelineClientBase | ||
|
@@ -36,16 +37,20 @@ | |
RequestIdPolicy, | ||
AsyncRetryPolicy, | ||
) | ||
from ._pipeline_client import _prepare_request | ||
from .pipeline._tools_async import to_rest_response as _to_rest_response | ||
|
||
try: | ||
from typing import TYPE_CHECKING | ||
from typing import TYPE_CHECKING, TypeVar | ||
except ImportError: | ||
TYPE_CHECKING = False | ||
|
||
HTTPRequestType = TypeVar("HTTPRequestType") | ||
AsyncHTTPResponseType = TypeVar("AsyncHTTPResponseType") | ||
|
||
if TYPE_CHECKING: | ||
from typing import ( | ||
List, | ||
Any, | ||
Dict, | ||
Union, | ||
IO, | ||
|
@@ -168,3 +173,56 @@ def _build_pipeline(self, config, **kwargs): # pylint: disable=no-self-use | |
transport = AioHttpTransport(**kwargs) | ||
|
||
return AsyncPipeline(transport, policies) | ||
|
||
async def _make_pipeline_call(self, request, **kwargs): | ||
rest_request, request_to_run = _prepare_request(request) | ||
return_pipeline_response = kwargs.pop("_return_pipeline_response", False) | ||
pipeline_response = await self._pipeline.run( | ||
request_to_run, **kwargs # pylint: disable=protected-access | ||
) | ||
response = pipeline_response.http_response | ||
if rest_request: | ||
rest_response = _to_rest_response(response) | ||
if not kwargs.get("stream"): | ||
try: | ||
# in this case, the pipeline transport response already called .load_body(), so | ||
# the body is loaded. instead of doing response.read(), going to set the body | ||
# to the internal content | ||
rest_response._content = response.body() # pylint: disable=protected-access | ||
await rest_response.close() | ||
except Exception as exc: | ||
await rest_response.close() | ||
raise exc | ||
response = rest_response | ||
if return_pipeline_response: | ||
pipeline_response.http_response = response | ||
pipeline_response.http_request = request | ||
return pipeline_response | ||
return response | ||
|
||
def send_request( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not async? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, it's a little confusing, but it returns an awaitable object. This has to do with the fact that we're returning a context manager that both has an response = await client.send_request(request) and like this (mainly in the case of streaming) async with client.send_request(request) as response:
print(response) talked through this with @johanste and he's ok with not making it |
||
self, | ||
request: HTTPRequestType, | ||
*, | ||
stream: bool = False, | ||
**kwargs: Any | ||
) -> Awaitable[AsyncHTTPResponseType]: | ||
"""**Provisional** method that runs the network request through the client's chained policies. | ||
|
||
This method is marked as **provisional**, meaning it may be changed in a future release. | ||
|
||
>>> from azure.core.rest import HttpRequest | ||
>>> request = HttpRequest('GET', 'http://www.example.com') | ||
<HttpRequest [GET], url: 'http://www.example.com'> | ||
>>> response = await client.send_request(request) | ||
<AsyncHttpResponse: 200 OK> | ||
|
||
:param request: The network request you want to make. Required. | ||
:type request: ~azure.core.rest.HttpRequest | ||
:keyword bool stream: Whether the response payload will be streamed. Defaults to False. | ||
:return: The response of your network call. Does not do error handling on your response. | ||
:rtype: ~azure.core.rest.AsyncHttpResponse | ||
""" | ||
from .rest._rest_py3 import _AsyncContextManager | ||
wrapped = self._make_pipeline_call(request, stream=stream, **kwargs) | ||
return _AsyncContextManager(wrapped=wrapped) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,3 +32,40 @@ def await_result(func, *args, **kwargs): | |
"Policy {} returned awaitable object in non-async pipeline.".format(func) | ||
) | ||
return result | ||
|
||
def to_rest_request(pipeline_transport_request): | ||
from ..rest import HttpRequest as RestHttpRequest | ||
return RestHttpRequest( | ||
method=pipeline_transport_request.method, | ||
url=pipeline_transport_request.url, | ||
headers=pipeline_transport_request.headers, | ||
files=pipeline_transport_request.files, | ||
data=pipeline_transport_request.data | ||
) | ||
|
||
def to_rest_response(pipeline_transport_response): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will this method do if users have created their client with a custom transport? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if they create their client with a custom transport, since i don't have a corresponding rest transport response to change it to, I change it to the default |
||
from .transport._requests_basic import RequestsTransportResponse | ||
from ..rest._requests_basic import RestRequestsTransportResponse | ||
from ..rest import HttpResponse | ||
if isinstance(pipeline_transport_response, RequestsTransportResponse): | ||
response_type = RestRequestsTransportResponse | ||
else: | ||
response_type = HttpResponse | ||
response = response_type( | ||
request=to_rest_request(pipeline_transport_response.request), | ||
internal_response=pipeline_transport_response.internal_response, | ||
) | ||
response._connection_data_block_size = pipeline_transport_response.block_size # pylint: disable=protected-access | ||
return response | ||
|
||
def get_block_size(response): | ||
try: | ||
return response._connection_data_block_size # pylint: disable=protected-access | ||
except AttributeError: | ||
return response.block_size | ||
|
||
def get_internal_response(response): | ||
try: | ||
return response._internal_response # pylint: disable=protected-access | ||
except AttributeError: | ||
return response.internal_response |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
# IN THE SOFTWARE. | ||
# | ||
# -------------------------------------------------------------------------- | ||
from ._tools import to_rest_request | ||
|
||
async def await_result(func, *args, **kwargs): | ||
"""If func returns an awaitable, await it.""" | ||
|
@@ -31,3 +32,37 @@ async def await_result(func, *args, **kwargs): | |
# type ignore on await: https://github.com/python/mypy/issues/7587 | ||
return await result # type: ignore | ||
return result | ||
|
||
def _get_response_type(pipeline_transport_response): | ||
try: | ||
from .transport import AioHttpTransportResponse | ||
from ..rest._aiohttp import RestAioHttpTransportResponse | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we support (or plan to support) custom transport? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so what I'm doing here is "if . However, I can't do a specific corresponding rest transport response for a custom transport since idk what that transport is, so I fall back to just creating the default Our goal in a later PR (hopefully in the next release cycle) is to fix the need to transform rest requests and responses, and somehow have our pipelines support both the old and new requests and responses. So this code is also temporary. |
||
if isinstance(pipeline_transport_response, AioHttpTransportResponse): | ||
return RestAioHttpTransportResponse | ||
except ImportError: | ||
pass | ||
try: | ||
from .transport import AsyncioRequestsTransportResponse | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to import all of them? Not only needed in except ImportError? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i can add a check for each of these to only try to import if response_type is still There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, I now stop importing once I get a reseponse type. Thanks for the suggestion @xiangyan99 ! |
||
from ..rest._requests_asyncio import RestAsyncioRequestsTransportResponse | ||
if isinstance(pipeline_transport_response, AsyncioRequestsTransportResponse): | ||
return RestAsyncioRequestsTransportResponse | ||
except ImportError: | ||
pass | ||
try: | ||
from .transport import TrioRequestsTransportResponse | ||
from ..rest._requests_trio import RestTrioRequestsTransportResponse | ||
if isinstance(pipeline_transport_response, TrioRequestsTransportResponse): | ||
return RestTrioRequestsTransportResponse | ||
except ImportError: | ||
pass | ||
from ..rest import AsyncHttpResponse | ||
return AsyncHttpResponse | ||
|
||
def to_rest_response(pipeline_transport_response): | ||
response_type = _get_response_type(pipeline_transport_response) | ||
response = response_type( | ||
request=to_rest_request(pipeline_transport_response.request), | ||
internal_response=pipeline_transport_response.internal_response, | ||
) | ||
response._connection_data_block_size = pipeline_transport_response.block_size # pylint: disable=protected-access | ||
return response |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xiangyan99 asked @johanste and he says it should be 1.16.0