Drop StreamContextManager in favour of contextlib.contextmanager/asynccontextmanager (#1577)
* Drop StreamContextManager in favour of using contextlib.contextmanager/asyncontextmanager * Use type: ignore to avoid mypy errors on 3.6
This commit is contained in:
parent
ed19995747
commit
8fd5b71016
@ -122,6 +122,7 @@ The HTTPX project relies on these excellent libraries:
|
||||
* `rfc3986` - URL parsing & normalization.
|
||||
* `idna` - Internationalized domain name support.
|
||||
* `sniffio` - Async library autodetection.
|
||||
* `async_generator` - Backport support for `contextlib.asynccontextmanager`. *(Only required for Python 3.6)*
|
||||
* `brotlipy` - Decoding for "brotli" compressed responses. *(Optional)*
|
||||
|
||||
A huge amount of credit is due to `requests` for the API layout that
|
||||
|
||||
@ -114,6 +114,7 @@ The HTTPX project relies on these excellent libraries:
|
||||
* `rfc3986` - URL parsing & normalization.
|
||||
* `idna` - Internationalized domain name support.
|
||||
* `sniffio` - Async library autodetection.
|
||||
* `async_generator` - Backport support for `contextlib.asynccontextmanager`. *(Only required for Python 3.6)*
|
||||
* `brotlipy` - Decoding for "brotli" compressed responses. *(Optional)*
|
||||
|
||||
A huge amount of credit is due to `requests` for the API layout that
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import typing
|
||||
from contextlib import contextmanager
|
||||
|
||||
from ._client import Client, StreamContextManager
|
||||
from ._client import Client
|
||||
from ._config import DEFAULT_TIMEOUT_CONFIG
|
||||
from ._models import Request, Response
|
||||
from ._models import Response
|
||||
from ._types import (
|
||||
AuthTypes,
|
||||
CertTypes,
|
||||
@ -106,6 +107,7 @@ def request(
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def stream(
|
||||
method: str,
|
||||
url: URLTypes,
|
||||
@ -124,7 +126,7 @@ def stream(
|
||||
verify: VerifyTypes = True,
|
||||
cert: CertTypes = None,
|
||||
trust_env: bool = True,
|
||||
) -> StreamContextManager:
|
||||
) -> typing.Iterator[Response]:
|
||||
"""
|
||||
Alternative to `httpx.request()` that streams the response body
|
||||
instead of loading it into memory at once.
|
||||
@ -135,26 +137,23 @@ def stream(
|
||||
|
||||
[0]: /quickstart#streaming-responses
|
||||
"""
|
||||
client = Client(proxies=proxies, cert=cert, verify=verify, trust_env=trust_env)
|
||||
request = Request(
|
||||
method=method,
|
||||
url=url,
|
||||
params=params,
|
||||
content=content,
|
||||
data=data,
|
||||
files=files,
|
||||
json=json,
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
)
|
||||
return StreamContextManager(
|
||||
client=client,
|
||||
request=request,
|
||||
auth=auth,
|
||||
timeout=timeout,
|
||||
allow_redirects=allow_redirects,
|
||||
close_client=True,
|
||||
)
|
||||
with Client(
|
||||
proxies=proxies, cert=cert, verify=verify, trust_env=trust_env
|
||||
) as client:
|
||||
with client.stream(
|
||||
method=method,
|
||||
url=url,
|
||||
content=content,
|
||||
data=data,
|
||||
files=files,
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
) as response:
|
||||
yield response
|
||||
|
||||
|
||||
def get(
|
||||
|
||||
208
httpx/_client.py
208
httpx/_client.py
@ -2,10 +2,12 @@ import datetime
|
||||
import enum
|
||||
import typing
|
||||
import warnings
|
||||
from contextlib import contextmanager
|
||||
from types import TracebackType
|
||||
|
||||
from .__version__ import __version__
|
||||
from ._auth import Auth, BasicAuth, FunctionAuth
|
||||
from ._compat import asynccontextmanager
|
||||
from ._config import (
|
||||
DEFAULT_LIMITS,
|
||||
DEFAULT_MAX_REDIRECTS,
|
||||
@ -289,51 +291,6 @@ class BaseClient:
|
||||
def params(self, params: QueryParamTypes) -> None:
|
||||
self._params = QueryParams(params)
|
||||
|
||||
def stream(
|
||||
self,
|
||||
method: str,
|
||||
url: URLTypes,
|
||||
*,
|
||||
content: RequestContent = None,
|
||||
data: RequestData = None,
|
||||
files: RequestFiles = None,
|
||||
json: typing.Any = None,
|
||||
params: QueryParamTypes = None,
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
|
||||
allow_redirects: bool = True,
|
||||
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
|
||||
) -> "StreamContextManager":
|
||||
"""
|
||||
Alternative to `httpx.request()` that streams the response body
|
||||
instead of loading it into memory at once.
|
||||
|
||||
**Parameters**: See `httpx.request`.
|
||||
|
||||
See also: [Streaming Responses][0]
|
||||
|
||||
[0]: /quickstart#streaming-responses
|
||||
"""
|
||||
request = self.build_request(
|
||||
method=method,
|
||||
url=url,
|
||||
content=content,
|
||||
data=data,
|
||||
files=files,
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
)
|
||||
return StreamContextManager(
|
||||
client=self,
|
||||
request=request,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
def build_request(
|
||||
self,
|
||||
method: str,
|
||||
@ -793,6 +750,56 @@ class Client(BaseClient):
|
||||
request, auth=auth, allow_redirects=allow_redirects, timeout=timeout
|
||||
)
|
||||
|
||||
@contextmanager
|
||||
def stream(
|
||||
self,
|
||||
method: str,
|
||||
url: URLTypes,
|
||||
*,
|
||||
content: RequestContent = None,
|
||||
data: RequestData = None,
|
||||
files: RequestFiles = None,
|
||||
json: typing.Any = None,
|
||||
params: QueryParamTypes = None,
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
|
||||
allow_redirects: bool = True,
|
||||
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
|
||||
) -> typing.Iterator[Response]:
|
||||
"""
|
||||
Alternative to `httpx.request()` that streams the response body
|
||||
instead of loading it into memory at once.
|
||||
|
||||
**Parameters**: See `httpx.request`.
|
||||
|
||||
See also: [Streaming Responses][0]
|
||||
|
||||
[0]: /quickstart#streaming-responses
|
||||
"""
|
||||
request = self.build_request(
|
||||
method=method,
|
||||
url=url,
|
||||
content=content,
|
||||
data=data,
|
||||
files=files,
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
)
|
||||
response = self.send(
|
||||
request=request,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
timeout=timeout,
|
||||
stream=True,
|
||||
)
|
||||
try:
|
||||
yield response
|
||||
finally:
|
||||
response.close()
|
||||
|
||||
def send(
|
||||
self,
|
||||
request: Request,
|
||||
@ -1430,6 +1437,56 @@ class AsyncClient(BaseClient):
|
||||
)
|
||||
return response
|
||||
|
||||
@asynccontextmanager
|
||||
async def stream(
|
||||
self,
|
||||
method: str,
|
||||
url: URLTypes,
|
||||
*,
|
||||
content: RequestContent = None,
|
||||
data: RequestData = None,
|
||||
files: RequestFiles = None,
|
||||
json: typing.Any = None,
|
||||
params: QueryParamTypes = None,
|
||||
headers: HeaderTypes = None,
|
||||
cookies: CookieTypes = None,
|
||||
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
|
||||
allow_redirects: bool = True,
|
||||
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
|
||||
) -> typing.AsyncIterator[Response]:
|
||||
"""
|
||||
Alternative to `httpx.request()` that streams the response body
|
||||
instead of loading it into memory at once.
|
||||
|
||||
**Parameters**: See `httpx.request`.
|
||||
|
||||
See also: [Streaming Responses][0]
|
||||
|
||||
[0]: /quickstart#streaming-responses
|
||||
"""
|
||||
request = self.build_request(
|
||||
method=method,
|
||||
url=url,
|
||||
content=content,
|
||||
data=data,
|
||||
files=files,
|
||||
json=json,
|
||||
params=params,
|
||||
headers=headers,
|
||||
cookies=cookies,
|
||||
)
|
||||
response = await self.send(
|
||||
request=request,
|
||||
auth=auth,
|
||||
allow_redirects=allow_redirects,
|
||||
timeout=timeout,
|
||||
stream=True,
|
||||
)
|
||||
try:
|
||||
yield response
|
||||
finally:
|
||||
await response.aclose()
|
||||
|
||||
async def send(
|
||||
self,
|
||||
request: Request,
|
||||
@ -1869,64 +1926,3 @@ class AsyncClient(BaseClient):
|
||||
"See https://www.python-httpx.org/async/#opening-and-closing-clients "
|
||||
"for details."
|
||||
)
|
||||
|
||||
|
||||
class StreamContextManager:
|
||||
def __init__(
|
||||
self,
|
||||
client: BaseClient,
|
||||
request: Request,
|
||||
*,
|
||||
auth: typing.Union[AuthTypes, UnsetType] = UNSET,
|
||||
allow_redirects: bool = True,
|
||||
timeout: typing.Union[TimeoutTypes, UnsetType] = UNSET,
|
||||
close_client: bool = False,
|
||||
) -> None:
|
||||
self.client = client
|
||||
self.request = request
|
||||
self.auth = auth
|
||||
self.allow_redirects = allow_redirects
|
||||
self.timeout = timeout
|
||||
self.close_client = close_client
|
||||
|
||||
def __enter__(self) -> "Response":
|
||||
assert isinstance(self.client, Client)
|
||||
self.response = self.client.send(
|
||||
request=self.request,
|
||||
auth=self.auth,
|
||||
allow_redirects=self.allow_redirects,
|
||||
timeout=self.timeout,
|
||||
stream=True,
|
||||
)
|
||||
return self.response
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: typing.Type[BaseException] = None,
|
||||
exc_value: BaseException = None,
|
||||
traceback: TracebackType = None,
|
||||
) -> None:
|
||||
assert isinstance(self.client, Client)
|
||||
self.response.close()
|
||||
if self.close_client:
|
||||
self.client.close()
|
||||
|
||||
async def __aenter__(self) -> "Response":
|
||||
assert isinstance(self.client, AsyncClient)
|
||||
self.response = await self.client.send(
|
||||
request=self.request,
|
||||
auth=self.auth,
|
||||
allow_redirects=self.allow_redirects,
|
||||
timeout=self.timeout,
|
||||
stream=True,
|
||||
)
|
||||
return self.response
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: typing.Type[BaseException] = None,
|
||||
exc_value: BaseException = None,
|
||||
traceback: TracebackType = None,
|
||||
) -> None:
|
||||
assert isinstance(self.client, AsyncClient)
|
||||
await self.response.aclose()
|
||||
|
||||
6
httpx/_compat.py
Normal file
6
httpx/_compat.py
Normal file
@ -0,0 +1,6 @@
|
||||
# `contextlib.asynccontextmanager` exists from Python 3.7 onwards.
|
||||
# For 3.6 we require the `async_generator` package for a backported version.
|
||||
try:
|
||||
from contextlib import asynccontextmanager # type: ignore
|
||||
except ImportError: # pragma: no cover
|
||||
from async_generator import asynccontextmanager # type: ignore # noqa
|
||||
Loading…
Reference in New Issue
Block a user