autoasync
Wrap synchronous work so it starts in the background and returns immediately.
The call only blocks when you actually use the result.
from autoasync import autoasync
def load_report(path):
...
load_report_async = autoasync(load_report)
report = load_report_async("report.csv")
prepare_page() # runs while load_report works in background
print(report) # blocks here only if the result is not ready yet
Why use this instead of async / await?
autoasync is mainly for retrofitting concurrency into existing synchronous code.
Compared with Python's built-in async / await, its main advantages are:
- No async contagion: callers can stay synchronous, so you do not need to turn the whole call chain into
async defjust to overlap one slow step. - Minimal code changes: wrapping a function with
@autoasyncorautoasync(fn)is often enough to start work in the background. - Deferred waiting: you do not have to decide upfront where to
await; execution only blocks when the value is actually needed. - Easy incremental optimization: this is convenient when improving a mature sync codebase, because you can add concurrency without redesigning APIs around an event loop.
That trade-off is intentional: this abstraction adds overhead, so it is not a replacement for native async / await in high-throughput async systems. It is most useful when you want a simple, low-friction way to hide latency in otherwise synchronous code.
Install
pip install autoasync
Usage
Decorate any synchronous function
from autoasync import autoasync
@autoasync
def fetch(url: str) -> str:
import requests
return requests.get(url).text
result = fetch("https://example.com")
do_other_work()
print(result)
Run several calls concurrently
a = fetch("https://example.com/a")
b = fetch("https://example.com/b")
c = fetch("https://example.com/c")
combined = a + "\n" + b + "\n" + c
CPU-bound work with processes
use_process=True is only supported for importable module-level functions.
from autoasync import autoasync
@autoasync(use_process=True)
def crunch(n: int) -> int:
return sum(range(n))
result = crunch(10_000_000)
do_other_work()
print(result)
Custom executor
from concurrent.futures import ThreadPoolExecutor
from autoasync import autoasync
pool = ThreadPoolExecutor(max_workers=4)
@autoasync(executor=pool)
def read(path: str) -> str:
with open(path) as f:
return f.read()
Configure built-in executors globally
Use configure_autoasync(...) when you want to keep the library-managed executors but control their default sizes.
from autoasync import autoasync, configure_autoasync
configure_autoasync(thread_max_workers=8, process_max_workers=4)
@autoasync
def fetch(url):
...
@autoasync(use_process=True)
def crunch(n):
return sum(range(n))
page = fetch("https://example.com") # uses the configured thread pool
total = crunch(10_000_000) # uses the configured process pool
Configuration changes apply to future built-in pools only. Existing cached pools are not replaced.
If you pass a custom executor, the global built-in pool configuration is ignored.
Use reset_autoasync() to clear cached built-in pools and restore default configuration, which is especially useful in tests.
Access the original function
@autoasync
def add(a, b):
return a + b
add.__wrapped__(1, 2)
What the returned value supports
The returned value behaves like the final result for most common Python protocols, including:
- attribute access
- arithmetic and comparisons
- container access and iteration
- conversions such as
int,float,str,bytes, andbool - context managers
open()path usage through__fspath__
The background result is resolved once and then cached.
Important behavior
is is special
is checks object identity and cannot be overloaded in Python. That means:
resolve_true = autoasync(lambda: True)
result = resolve_true()
result == True # works
bool(result) is True # works
result is True # False: `is` checks identity, not the eventual value
If you need identity-style checks, resolve first or use == / bool(...).
Exceptions are deferred
If the wrapped function raises an exception, the exception is not raised at call time.
It is re-raised when the result is first needed.
@autoasync
def explode():
raise RuntimeError("boom")
result = explode() # no exception yet
print(result) # raises RuntimeError here
Process mode restrictions
When use_process=True, the wrapped function must be:
- defined at module scope
- importable from its module
- a normal function, not a lambda, nested function, bound method, or callable object
- optionally paired with
configure_autoasync(process_max_workers=...)to size the built-in process pool
Unsupported targets raise a clear TypeError.
How it works
result = slow_fn(arg) # submits fn(arg) to an executor
# returns immediately
do_other_work() # runs concurrently with fn(arg)
print(result + 1) # blocks only here if needed
Requirements
- Python 3.8+
- No runtime dependencies
License
MIT
Metadata
Release files for autoasync 0.1.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| autoasync-0.1.3.tar.gz | 14.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| autoasync-0.1.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 23.9 kB
Release files / autoasync-0.1.3.tar.gz
| Download URL | autoasync-0.1.3.tar.gz |
|---|---|
| Size | 14.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f5a5c9dd280d1e1c5910bc5e1dcb6e064494ab4530cc62c305bf082a47690005
|
|
BLAKE2b-256 checksum How to use checksums |
cd9478b0b92123b33ad7a7bfe206e8af29a2e05f69594ab29ebfa09c2441a3b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 25, 2026.
Transparency logRelease files / autoasync-0.1.3-py3-none-any.whl
| Download URL | autoasync-0.1.3-py3-none-any.whl |
|---|---|
| Size | 9.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
140d8e7f2d74c624ae40367adc5f65c45e5581ba9315cc839c3160c4f34f631f
|
|
BLAKE2b-256 checksum How to use checksums |
0fa28a03ee2b1363794b2bb8e9c9a5f20f65af204738094eacd14d58a1b9b43b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 25, 2026.
Transparency logSecurity for autoasync 0.1.3
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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for autoasync-0.1.3.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Apr 25, 2026.
Transparency logA 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.