Skip to content

Commit

Permalink
Move body logic to Auth, add sync_auth_flow, add NoAuth
Browse files Browse the repository at this point in the history
  • Loading branch information
florimondmanca committed Aug 27, 2020
1 parent be809fb commit 3432c14
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 19 deletions.
43 changes: 36 additions & 7 deletions httpx/_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,10 @@ class Auth:
To implement a custom authentication scheme, subclass `Auth` and override
the `.auth_flow()` method.
If the authentication scheme does I/O, such as disk access or network calls, or uses
synchronization primitives such as locks, you should override `.async_auth_flow()`
to provide an async-friendly implementation that will be used by the `AsyncClient`.
Usage of sync I/O within an async codebase would block the event loop, and could
cause performance issues.
If the authentication scheme does I/O such as disk access or network calls, or uses
synchronization primitives such as locks, you should override `.sync_auth_flow()`
and/or `.async_auth_flow()` instead of `.auth_flow()` to provide specialized
implementations that will be used by `Client` and `AsyncClient` respectively.
"""

requires_request_body = False
Expand Down Expand Up @@ -52,21 +51,51 @@ def auth_flow(self, request: Request) -> typing.Generator[Request, Response, Non
"""
yield request

def sync_auth_flow(
self, request: Request
) -> typing.Generator[Request, Response, None]:
"""
Execute the authentication flow synchronously.
By default, this defers to `.auth_flow()`. You should override this method
when the authentication scheme does I/O and/or uses concurrency primitives.
"""
if self.requires_request_body:
request.read()

flow = self.auth_flow(request)
request = next(flow)

while True:
response = yield request
if self.requires_response_body:
response.read()

try:
request = flow.send(response)
except StopIteration:
break

async def async_auth_flow(
self, request: Request
) -> typing.AsyncGenerator[Request, Response]:
"""
Execute the authentication flow asynchronously.
By default, this defers to `.auth_flow()`. You should override this method
when the authentication scheme does I/O, such as disk access or network calls,
or uses concurrency primitives such as locks.
when the authentication scheme does I/O and/or uses concurrency primitives.
"""
if self.requires_request_body:
await request.aread()

flow = self.auth_flow(request)
request = next(flow)

while True:
response = yield request
if self.requires_response_body:
await response.aread()

try:
request = flow.send(response)
except StopIteration:
Expand Down
18 changes: 6 additions & 12 deletions httpx/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -760,15 +760,12 @@ def _send_handling_auth(
auth: Auth,
timeout: Timeout,
) -> Response:
if auth.requires_request_body:
request.read()
auth_flow = auth.sync_auth_flow(request)
request = auth_flow.send(None) # type: ignore

auth_flow = auth.auth_flow(request)
request = next(auth_flow)
while True:
response = self._send_single_request(request, timeout)
if auth.requires_response_body:
response.read()

try:
next_request = auth_flow.send(response)
except StopIteration:
Expand Down Expand Up @@ -1362,15 +1359,12 @@ async def _send_handling_auth(
auth: Auth,
timeout: Timeout,
) -> Response:
if auth.requires_request_body:
await request.aread()

auth_flow = auth.async_auth_flow(request)
request = await auth_flow.__anext__()
request = await auth_flow.asend(None) # type: ignore

while True:
response = await self._send_single_request(request, timeout)
if auth.requires_response_body:
await response.aread()

try:
next_request = await auth_flow.asend(response)
except StopAsyncIteration:
Expand Down

0 comments on commit 3432c14

Please sign in to comment.