From 3ddc9ddc63b7d065586881836e443f4a51d2f32e Mon Sep 17 00:00:00 2001 From: florimondmanca Date: Sun, 17 Nov 2019 11:43:59 +0100 Subject: [PATCH 1/2] Document when to use AsyncClient --- docs/async.md | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/docs/async.md b/docs/async.md index 45620bf8c2..9161669dff 100644 --- a/docs/async.md +++ b/docs/async.md @@ -105,3 +105,78 @@ trio.run(main) !!! important `trio` must be installed to import and use the `TrioBackend`. + +## FAQ + +### When should I use an `AsyncClient`? + +You should use an `AsyncClient` whenever you are inside an *async environment*. + +In particular, using `httpx.get()` or `httpx.Client()` in an async environment is **not** supported. There are several technical reasons to this, but the rationale is that you shouldn't be doing blocking-style network calls in the context of an event loop (for more discussion, see [#179](https://github.com/encode/httpx/issues/179)). + +For example, this won't work: + +```python +import asyncio +import httpx + +async def main(): + r = httpx.get("https://example.org") + +asyncio.run(main()) +``` + +Instead, you should use an `AsyncClient`: + +```python +import asyncio +import httpx + +async def main(): + async with httpx.AsyncClient() as client: + r = await client.get("https://example.org") + +asyncio.run(main()) +``` + +Note that being in an async environment may not be immediately obvious. For example, you may be in a regular function that is called from an asynchronous function. So, this won't work either: + +```python +import asyncio +import httpx + +class Service: + def fetch_data(self): + r = httpx.get("https://example.org") + return r.text + +async def main(): + service = Service() + data = service.fetch_data() + +asyncio.run(main()) +``` + +Here, you should make `.fetch_data()` asynchronous. To use a shared client and benefit from connection pooling, you can also inject the `AsyncClient` as a parameter. This leads to the following code: + +```python +import asyncio +import httpx + +class Service: + async def fetch_data(self, client): + r = await client.get("https://example.org") + return r.text + +async def main(): + service = Service() + async with httpx.AsyncClient() as client: + data = await service.fetch_data(client) + more_data = await service.fetch_data(client) + +asyncio.run(main()) +``` + +If you do not have control over making `.fetch_data()` asynchronous (for example, if it is a synchronous hook provided by a third-party framework), then the only solution would be to make the HTTP request in a separate thread. At this point, you should probably consider using a regular threaded HTTP client such as [Requests](https://github.com/psf/requests). + +Lastly, if you are using HTTPX in the Jupyter REPL, you should also use an `AsyncClient`, as the Jupyter REPL secretly runs an event loop and lets you use `await` directly. For related discussion, see [#508](https://github.com/encode/httpx/issues/508)). From c93c02d15beb03c8b37a47b9238cb5664a474dcb Mon Sep 17 00:00:00 2001 From: florimondmanca Date: Fri, 22 Nov 2019 09:13:53 +0100 Subject: [PATCH 2/2] Strip advice on reverting to Requests --- docs/async.md | 42 +----------------------------------------- 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/docs/async.md b/docs/async.md index 9161669dff..c631d54551 100644 --- a/docs/async.md +++ b/docs/async.md @@ -139,44 +139,4 @@ async def main(): asyncio.run(main()) ``` -Note that being in an async environment may not be immediately obvious. For example, you may be in a regular function that is called from an asynchronous function. So, this won't work either: - -```python -import asyncio -import httpx - -class Service: - def fetch_data(self): - r = httpx.get("https://example.org") - return r.text - -async def main(): - service = Service() - data = service.fetch_data() - -asyncio.run(main()) -``` - -Here, you should make `.fetch_data()` asynchronous. To use a shared client and benefit from connection pooling, you can also inject the `AsyncClient` as a parameter. This leads to the following code: - -```python -import asyncio -import httpx - -class Service: - async def fetch_data(self, client): - r = await client.get("https://example.org") - return r.text - -async def main(): - service = Service() - async with httpx.AsyncClient() as client: - data = await service.fetch_data(client) - more_data = await service.fetch_data(client) - -asyncio.run(main()) -``` - -If you do not have control over making `.fetch_data()` asynchronous (for example, if it is a synchronous hook provided by a third-party framework), then the only solution would be to make the HTTP request in a separate thread. At this point, you should probably consider using a regular threaded HTTP client such as [Requests](https://github.com/psf/requests). - -Lastly, if you are using HTTPX in the Jupyter REPL, you should also use an `AsyncClient`, as the Jupyter REPL secretly runs an event loop and lets you use `await` directly. For related discussion, see [#508](https://github.com/encode/httpx/issues/508)). +If you *need* to make synchronous requests, or otherwise run into issues related to sync usage, you should probably consider using a regular threaded client such as [Requests](https://github.com/psf/requests) instead.