Skip to main content

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 def just to overlap one slow step.
  • Minimal code changes: wrapping a function with @autoasync or autoasync(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, and bool
  • 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)

Source distribution for autoasync 0.1.3
File Size Uploaded Provenance
autoasync-0.1.3.tar.gz 14.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for autoasync 0.1.3
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

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.

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files Full 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