home / tils / til

Menu
  • GraphQL API

til: asgi_lifespan-test-httpx.md

This data as json

path topic title url body html shot created created_utc updated updated_utc shot_hash slug
asgi_lifespan-test-httpx.md asgi Writing tests for the ASGI lifespan protocol with HTTPX https://github.com/simonw/til/blob/main/asgi/lifespan-test-httpx.md Uvicorn silently ignores exceptions that occur during startup against the ASGI lifespan protocol - see [starlette/issues/486](https://github.com/encode/starlette/issues/486). You can disable this feature using the `lifespan="on"` parameter to `uvicorn.run()` - which Datasette now does as-of [16f592247a2a0e140ada487e9972645406dcae69](https://github.com/simonw/datasette/commit/16f592247a2a0e140ada487e9972645406dcae69) This exposed a bug in `datasette-debug-asgi`: it wasn't handling lifespan events correctly. [datasette-debug-asgi/issues/1](https://github.com/simonw/datasette-debug-asgi/issues/1) The unit tests weren't catching this because using HTTPX to make test requests [doesn't trigger lifespan events](https://github.com/encode/httpx/issues/350). Florimond Manca had run into this problem too, and built [asgi-lifespan](https://github.com/florimondmanca/asgi-lifespan) to address it. You can wrap an ASGI app in `async with LifespanManager(app):` and the correct lifespan events will be fired by that with block. Here's how to use it to [trigger lifespan events in a test](https://github.com/simonw/datasette-debug-asgi/blob/72d568d32a3159c763ce908c0b269736935c6987/test_datasette_debug_asgi.py): ```python from asgi_lifespan import LifespanManager @pytest.mark.asyncio async def test_datasette_debug_asgi(): ds = Datasette([], memory=True) app = ds.app() async with LifespanManager(app): async with httpx.AsyncClient(app=app) as client: response = await client.get("http://localhost/-/asgi-scope") assert 200 == response.status_code assert "text/plain; charset=UTF-8" == response.headers["content-type"] ``` <p>Uvicorn silently ignores exceptions that occur during startup against the ASGI lifespan protocol - see <a href="https://github.com/encode/starlette/issues/486">starlette/issues/486</a>.</p> <p>You can disable this feature using the <code>lifespan="on"</code> parameter to <code>uvicorn.run()</code> - which Datasette now does as-of <a href="https://github.com/simonw/datasette/commit/16f592247a2a0e140ada487e9972645406dcae69">16f592247a2a0e140ada487e9972645406dcae69</a></p> <p>This exposed a bug in <code>datasette-debug-asgi</code>: it wasn't handling lifespan events correctly. <a href="https://github.com/simonw/datasette-debug-asgi/issues/1">datasette-debug-asgi/issues/1</a></p> <p>The unit tests weren't catching this because using HTTPX to make test requests <a href="https://github.com/encode/httpx/issues/350">doesn't trigger lifespan events</a>.</p> <p>Florimond Manca had run into this problem too, and built <a href="https://github.com/florimondmanca/asgi-lifespan">asgi-lifespan</a> to address it.</p> <p>You can wrap an ASGI app in <code>async with LifespanManager(app):</code> and the correct lifespan events will be fired by that with block.</p> <p>Here's how to use it to <a href="https://github.com/simonw/datasette-debug-asgi/blob/72d568d32a3159c763ce908c0b269736935c6987/test_datasette_debug_asgi.py">trigger lifespan events in a test</a>:</p> <div class="highlight highlight-source-python"><pre><span class="pl-k">from</span> <span class="pl-s1">asgi_lifespan</span> <span class="pl-k">import</span> <span class="pl-v">LifespanManager</span> <span class="pl-en">@<span class="pl-s1">pytest</span>.<span class="pl-s1">mark</span>.<span class="pl-s1">asyncio</span></span> <span class="pl-k">async</span> <span class="pl-k">def</span> <span class="pl-en">test_datasette_debug_asgi</span>(): <span class="pl-s1">ds</span> <span class="pl-c1">=</span> <span class="pl-v">Datasette</span>([], <span class="pl-s1">memory</span><span class="pl-c1">=</span><span class="pl-c1">True</span>) <span class="pl-s1">app</span> <span class="pl-c1">=</span> <span class="pl-s1">ds</span>.<span class="pl-en">app</span>() <span class="pl-k">async</span> <span class="pl-k">with</span> <span class="pl-v">LifespanManager</span>(<span class="pl-s1">app</span>): <span class="pl-k">async</span> <span class="pl-k">with</span> <span class="pl-s1">httpx</span>.<span class="pl-v">AsyncClient</span>(<span class="pl-s1">app</span><span class="pl-c1">=</span><span class="pl-s1">app</span>) <span class="pl-k">as</span> <span class="pl-s1">client</span>: <span class="pl-s1">response</span> <span class="pl-c1">=</span> <span class="pl-k">await</span> <span class="pl-s1">client</span>.<span class="pl-en">get</span>(<span class="pl-s">"http://localhost/-/asgi-scope"</span>) <span class="pl-k">assert</span> <span class="pl-c1">200</span> <span class="pl-c1">==</span> <span class="pl-s1">response</span>.<span class="pl-s1">status_code</span> <span class="pl-k">assert</span> <span class="pl-s">"text/plain; charset=UTF-8"</span> <span class="pl-c1">==</span> <span class="pl-s1">response</span>.<span class="pl-s1">headers</span>[<span class="pl-s">"content-type"</span>]</pre></div> <Binary: 79,669 bytes> 2020-06-29T09:13:49-07:00 2020-06-29T16:13:49+00:00 2020-07-06T13:46:05-07:00 2020-07-06T20:46:05+00:00 4656e3d750ad850e94a3240ebcbcbb26 lifespan-test-httpx
Powered by Datasette · How this site works · Code of conduct