Skip to content

Commit 878a12e

Browse files
authored
[rest] add response backcompat mixin (Azure#20827)
1 parent be52288 commit 878a12e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+1902
-691
lines changed

sdk/core/azure-core/CHANGELOG.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@
44

55
### Features Added
66

7-
### Breaking Changes
7+
### Breaking Changes in the Provisional `azure.core.rest` package
8+
9+
- `azure.core.rest.HttpResponse` and `azure.core.rest.AsyncHttpResponse` are now abstract base classes. They should not be initialized directly, instead
10+
your transport responses should inherit from them and implement them.
11+
- The properties of the `azure.core.rest` responses are now all read-only
812

913
- HttpLoggingPolicy integrates logs into one record #19925
1014

@@ -24,8 +28,6 @@
2428
- The `text` property on `azure.core.rest.HttpResponse` and `azure.core.rest.AsyncHttpResponse` has changed to a method, which also takes
2529
an `encoding` parameter.
2630
- Removed `iter_text` and `iter_lines` from `azure.core.rest.HttpResponse` and `azure.core.rest.AsyncHttpResponse`
27-
- `azure.core.rest.HttpResponse` and `azure.core.rest.AsyncHttpResponse` are now abstract base classes. They should not be initialized directly, instead
28-
your transport responses should inherit from them and implement them.
2931

3032
### Bugs Fixed
3133

sdk/core/azure-core/azure/core/_pipeline_client.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
RequestIdPolicy,
4040
RetryPolicy,
4141
)
42-
from .pipeline._tools import to_rest_response as _to_rest_response
4342

4443
try:
4544
from typing import TYPE_CHECKING
@@ -192,22 +191,10 @@ def send_request(self, request, **kwargs):
192191
:keyword bool stream: Whether the response payload will be streamed. Defaults to False.
193192
:return: The response of your network call. Does not do error handling on your response.
194193
:rtype: ~azure.core.rest.HttpResponse
195-
# """
196-
rest_request = hasattr(request, "content")
194+
"""
195+
stream = kwargs.pop("stream", False) # want to add default value
197196
return_pipeline_response = kwargs.pop("_return_pipeline_response", False)
198-
pipeline_response = self._pipeline.run(request, **kwargs) # pylint: disable=protected-access
199-
response = pipeline_response.http_response
200-
if rest_request:
201-
response = _to_rest_response(response)
202-
try:
203-
if not kwargs.get("stream", False):
204-
response.read()
205-
response.close()
206-
except Exception as exc:
207-
response.close()
208-
raise exc
197+
pipeline_response = self._pipeline.run(request, stream=stream, **kwargs) # pylint: disable=protected-access
209198
if return_pipeline_response:
210-
pipeline_response.http_response = response
211-
pipeline_response.http_request = request
212199
return pipeline_response
213-
return response
200+
return pipeline_response.http_response

sdk/core/azure-core/azure/core/_pipeline_client_async.py

Lines changed: 1 addition & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
RequestIdPolicy,
3838
AsyncRetryPolicy,
3939
)
40-
from .pipeline._tools_async import to_rest_response as _to_rest_response
4140

4241
try:
4342
from typing import TYPE_CHECKING, TypeVar
@@ -194,30 +193,13 @@ def _build_pipeline(self, config, **kwargs): # pylint: disable=no-self-use
194193
return AsyncPipeline(transport, policies)
195194

196195
async def _make_pipeline_call(self, request, **kwargs):
197-
rest_request = hasattr(request, "content")
198196
return_pipeline_response = kwargs.pop("_return_pipeline_response", False)
199197
pipeline_response = await self._pipeline.run(
200198
request, **kwargs # pylint: disable=protected-access
201199
)
202-
response = pipeline_response.http_response
203-
if rest_request:
204-
rest_response = _to_rest_response(response)
205-
if not kwargs.get("stream"):
206-
try:
207-
# in this case, the pipeline transport response already called .load_body(), so
208-
# the body is loaded. instead of doing response.read(), going to set the body
209-
# to the internal content
210-
rest_response._content = response.body() # pylint: disable=protected-access
211-
await rest_response._set_read_checks() # pylint: disable=protected-access
212-
except Exception as exc:
213-
await rest_response.close()
214-
raise exc
215-
response = rest_response
216200
if return_pipeline_response:
217-
pipeline_response.http_response = response
218-
pipeline_response.http_request = request
219201
return pipeline_response
220-
return response
202+
return pipeline_response.http_response
221203

222204
def send_request(
223205
self,

sdk/core/azure-core/azure/core/pipeline/_tools.py

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,11 @@
2323
# IN THE SOFTWARE.
2424
#
2525
# --------------------------------------------------------------------------
26+
from typing import TYPE_CHECKING
27+
28+
if TYPE_CHECKING:
29+
from typing import Any
30+
from azure.core.rest import HttpResponse as RestHttpResponse
2631

2732
def await_result(func, *args, **kwargs):
2833
"""If func returns an awaitable, raise that this runner can't handle it."""
@@ -33,38 +38,26 @@ def await_result(func, *args, **kwargs):
3338
)
3439
return result
3540

36-
def to_rest_request(pipeline_transport_request):
37-
from ..rest import HttpRequest as RestHttpRequest
38-
return RestHttpRequest(
39-
method=pipeline_transport_request.method,
40-
url=pipeline_transport_request.url,
41-
headers=pipeline_transport_request.headers,
42-
files=pipeline_transport_request.files,
43-
data=pipeline_transport_request.data
44-
)
45-
46-
def to_rest_response(pipeline_transport_response):
47-
from .transport._requests_basic import RequestsTransportResponse
48-
from ..rest._requests_basic import RestRequestsTransportResponse
49-
if isinstance(pipeline_transport_response, RequestsTransportResponse):
50-
response_type = RestRequestsTransportResponse
51-
else:
52-
raise ValueError("Unknown transport response")
53-
response = response_type(
54-
request=to_rest_request(pipeline_transport_response.request),
55-
internal_response=pipeline_transport_response.internal_response,
56-
block_size=pipeline_transport_response.block_size
57-
)
58-
return response
41+
def is_rest(obj):
42+
# type: (Any) -> bool
43+
"""Return whether a request or a response is a rest request / response.
5944
60-
def get_block_size(response):
61-
try:
62-
return response._block_size # pylint: disable=protected-access
63-
except AttributeError:
64-
return response.block_size
45+
Checking whether the response has the object content can sometimes result
46+
in a ResponseNotRead error if you're checking the value on a response
47+
that has not been read in yet. To get around this, we also have added
48+
a check for is_stream_consumed, which is an exclusive property on our new responses.
49+
"""
50+
return hasattr(obj, "is_stream_consumed") or hasattr(obj, "content")
6551

66-
def get_internal_response(response):
52+
def handle_non_stream_rest_response(response):
53+
# type: (RestHttpResponse) -> None
54+
"""Handle reading and closing of non stream rest responses.
55+
For our new rest responses, we have to call .read() and .close() for our non-stream
56+
responses. This way, we load in the body for users to access.
57+
"""
6758
try:
68-
return response._internal_response # pylint: disable=protected-access
69-
except AttributeError:
70-
return response.internal_response
59+
response.read()
60+
response.close()
61+
except Exception as exc:
62+
response.close()
63+
raise exc

sdk/core/azure-core/azure/core/pipeline/_tools_async.py

Lines changed: 14 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@
2323
# IN THE SOFTWARE.
2424
#
2525
# --------------------------------------------------------------------------
26-
from ._tools import to_rest_request
26+
from typing import TYPE_CHECKING
27+
if TYPE_CHECKING:
28+
from ..rest import AsyncHttpResponse as RestAsyncHttpResponse
29+
2730

2831
async def await_result(func, *args, **kwargs):
2932
"""If func returns an awaitable, await it."""
@@ -33,35 +36,14 @@ async def await_result(func, *args, **kwargs):
3336
return await result # type: ignore
3437
return result
3538

36-
def _get_response_type(pipeline_transport_response):
37-
try:
38-
from .transport import AioHttpTransportResponse
39-
from ..rest._aiohttp import RestAioHttpTransportResponse
40-
if isinstance(pipeline_transport_response, AioHttpTransportResponse):
41-
return RestAioHttpTransportResponse
42-
except ImportError:
43-
pass
39+
async def handle_no_stream_rest_response(response: "RestAsyncHttpResponse") -> None:
40+
"""Handle reading and closing of non stream rest responses.
41+
For our new rest responses, we have to call .read() and .close() for our non-stream
42+
responses. This way, we load in the body for users to access.
43+
"""
4444
try:
45-
from .transport import AsyncioRequestsTransportResponse
46-
from ..rest._requests_asyncio import RestAsyncioRequestsTransportResponse
47-
if isinstance(pipeline_transport_response, AsyncioRequestsTransportResponse):
48-
return RestAsyncioRequestsTransportResponse
49-
except ImportError:
50-
pass
51-
try:
52-
from .transport import TrioRequestsTransportResponse
53-
from ..rest._requests_trio import RestTrioRequestsTransportResponse
54-
if isinstance(pipeline_transport_response, TrioRequestsTransportResponse):
55-
return RestTrioRequestsTransportResponse
56-
except ImportError:
57-
pass
58-
raise ValueError("Unknown transport response")
59-
60-
def to_rest_response(pipeline_transport_response):
61-
response_type = _get_response_type(pipeline_transport_response)
62-
response = response_type(
63-
request=to_rest_request(pipeline_transport_response.request),
64-
internal_response=pipeline_transport_response.internal_response,
65-
block_size=pipeline_transport_response.block_size,
66-
)
67-
return response
45+
await response.read()
46+
await response.close()
47+
except Exception as exc:
48+
await response.close()
49+
raise exc

sdk/core/azure-core/azure/core/pipeline/policies/_universal.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,11 @@ def deserialize_from_http_generics(
617617
mime_type = "application/json"
618618

619619
# Rely on transport implementation to give me "text()" decoded correctly
620+
if hasattr(response, "read"):
621+
# since users can call deserialize_from_http_generics by themselves
622+
# we want to make sure our new responses are read before we try to
623+
# deserialize
624+
response.read()
620625
return cls.deserialize_from_text(response.text(encoding), mime_type, response=response)
621626

622627
def on_request(self, request):

0 commit comments

Comments
 (0)