Skip to main content

Awaitless

If you’re writing network-oriented code in Python, you’re probably looking at an asynchronous framework of some kind. Let’s say you’re also writing code that’s intended to be used interactively in IPython or Jupyter notebooks.

(Why would you ever use async interactively in IPython/JupyterLab? There’s a longer screed associated with an earlier attempt here.)

That’s a drag, because async is irrelevant for interactive use – except you need to sprinkle “await” and “async” keywords in exactly the right places or it doesn’t work.

For example, let’s pretend “release_kraken” is a complicated piece of async machinery. It might take arguments, interacts with the world, and might return a value. Here’s a placeholder definition:

>>> async def release_kraken():
...     import aiohttp, http
...     async with aiohttp.ClientSession() as cs:
...         resp = await cs.post('http://httpbin.org/post')
...         return resp.status == http.HTTPStatus.OK

Let’s say you’re calling release_kraken() in an interactive ipython session:

>>> release_kraken() # doctest: +SKIP
Out[1]: <coroutine object release_kraken ...>

The kraken is not released - your function never actually executes. All you get is a “RuntimeWarning: coroutine was never awaited” slap on the wrist. You forgot the “await”:

>>> await release_kraken()
Out[1]: True

That’s … better? I mean, it’s now “correct” according to asyncio conventions, and we’re making use of ipython’s autoawait magic, but there is never any ambiguity about a coroutine at the top level of an interactive session. The user wanted to run some code, and forcing them to slavishly type “await” every time is so pedantic it’s user-hostile.

Let’s do better:

>>> %load_ext awaitless
>>> release_kraken()
Out[1]: ...<Task finished ... result=True>

What did that do?

  • The async code ran to completion, even though we didn’t await it (note the ‘finished’ and ‘result’)

  • The return value is now a Task, not a coroutine(). However, both coroutines and Tasks are awaitable, so the switcheroo is (largely) API compatible.

Why?

Yes, “await” is only 6 extra keys to hit, but it’s distracting and unnecessary. You could have the same discussion about print()ing the results in a REPL rather than just displaying them – and ipython cares enough about this to make it configurable. (Try get_ipython().ast_node_interactivity='all' if you’re curious.)

I find myself returning to this article every year or so. The author says:

I ain’t gonna mock Tcl-scriptable tools no more. I understand what made the authors choose Tcl, and no, it’s not just a bad habit. On a level, they chose probably the best thing available today in terms of ease-of-programming/ease-of-use trade-off (assuming they bundle an interactive command shell). If I need to automate something related to those tools, I’ll delve into it more happily than previously.

In short: languages designed for interactive use (tcl, bash, etc) make syntax decisions that are different than languages that are designed for programming. Interactive languages tend to prioritize “shallow” tasks like command invocation at the expense of language composition (functions, classes, modules). IPython is a reasonable compromise - but not when coroutines are involved.

Testing

Run the following:

awaitless$ pytest

Metadata

Release files for awaitless 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for awaitless 1.0.0
File Size Uploaded Provenance Actions
awaitless-1.0.0.tar.gz 7.1 kB Without provenance Details for awaitless-1.0.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for awaitless 1.0.0
File Interpreter ABI Platform Actions
awaitless-1.0.0-py3-none-any.whl Python 3 none any Details for awaitless-1.0.0-py3-none-any.whl

Total release size: 12.1 kB

Release files / awaitless-1.0.0.tar.gz

Download URL awaitless-1.0.0.tar.gz
Size 7.1 kB
Tags Source
SHA-256 checksum
How to use checksums
0e33230b8c421c2e8c93fb9939995f46924fc3d62e17eab13dbfec824895a845
BLAKE2b-256 checksum
How to use checksums
9da16c02aee04c0d275f67ac1cda75d2787b536b6072cd3d16cf7af553b6dae1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/5.1.0 CPython/3.12.5

Release files / awaitless-1.0.0-py3-none-any.whl

Download URL awaitless-1.0.0-py3-none-any.whl
Size 4.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
27c9ff62733013aa5b794c3bad5aa4fccbbc1c614d2ae95f1f8da6649d8984a3
BLAKE2b-256 checksum
How to use checksums
5e2f067242220621a61e359ce8989555b628cbdb91070849ee3a8bf8a4020321
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/5.1.0 CPython/3.12.5

Security for awaitless 1.0.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page