Skip to main content

asyncrunner

Interface for running synchronous routines in python's async context

Generally, synchronous routines should not run in async context because the synchronous routines would block the async event loop. asyncrunner creates an isolated executor context in which these routines can run.

For example:

import time
import asyncrunner as a


class Bar:
    """A synchronous class"""
    def __init__(self, name: str) -> None:
        self.name = name

    def get_name(self) -> str:
        return self.name


class Foo:
    """Another synchronous class"""
    def __init__(self, num: int, name: str) -> None:
        time.sleep(1)
        self.num = num
        self.bar = Bar(name)

    def get_num(self) -> int:
        """A blocking method"""
        self.num += 1
        time.sleep(1)
        return self.num


async def spam() -> None:
    # Instead of creating an object using:
    # foo = Foo(3, "Joe)
    # we create an encapsulated object in a thread executor:
    foo_exe = a.create(a.Mode.THREAD, Foo, 3, "Joe")

    # Now we can call Foo's methods without blocking:
    assert await a.run(foo_exe.get_num) == 4
    assert await a.run(foo_exe.get_num) == 5

    # Accessing Foo's attributes as values requires attaching them first:
    await a.attach_value(foo_exe, "num")
    assert await a.get_value(foo_exe.num) == 5
    await a.set_value(foo_exe.num, 0)

    # Foo's attributes can also be attached as objects with their own methods:
    await a.attach_object(foo_exe, "bar")
    assert await a.run(foo_exe.bar.get_name) == "Joe"

The a.create routine returns an executor that encapsulates an instance of the Foo class. This executor has the same methods as the original instance (only get_num in this example). But these methods can only be accessed using the asynchronous a.run coroutine. This guarantees that these methods would not block the event loop.

Initially, the executor has access to the class methods, but not to the instance attributes (self.num and self.bar in this case). Instance attributes can be attached to the executor using the a.attach_value coroutine. Then a.set_value and a.get_value can be used to access these attributes.

Instance attributes can also be attached using a.attach_object. This enables running the attributes methods using a.run.

The class instance is hidden inside the executor. Actually, a.create avoids creating this instance since the __init__ method could be blocking. The instance is created on the first a.run, a.attach_object or a.attach_value call on the executor.

The instance methods are always executed in the same dedicated thread. This reduces the chances of concurrency issues due to accessing the same data from different threads. This risk is not eliminated, since you can still pass containers as arguments or return values. These containers will be shared between the main thread and the executor thread.

Instead of using threads to manage concurrency, it is possible to use a sub-process or a sub-interpreter, using a.Mode.PROCESS and a.Mode.INTERPRETER respectively. asyncrunner API is exactly the same for all these concurrency models. There are some subtle behavior differences between threads and sub-processes/interpreters due to the fact that the latter are completely issolated.

All data passed to sub-processes/interpreters is pickled. Non-picklable data, such as file handlers, cannot be passed. Containers, such as lists, can be passed, if all their content is picklable. But the other end receives a copy of the container. Therefore, the containers are completely issolated as opposed to the case of threads where the containers were shared.

asyncrunner has a test suite with 100% coverage. Hopefully, this test suite covers all the corner cases that this package could encounter.

Internally, concurrency is managed with concurrent.futures.Executor using an executor pool with a single worker.

Installation

asyncrunner can be installed from pypi:

pip3 install asyncrunner

asyncrunner requires python 3.12 or newer. Using a sub-interpreter executor requires python 3.14 or newer. There are no external dependencies.

History

0.1.0 (2026-09-15)

  • Initial release.

Metadata

Release files for asyncrunner-executor 0.1

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

Source distribution (sdist)

Source distribution for asyncrunner-executor 0.1
File Size Uploaded Provenance
asyncrunner_executor-0.1.tar.gz 7.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for asyncrunner-executor 0.1
File Interpreter ABI Platform
asyncrunner_executor-0.1-py3-none-any.whl Python 3 none any Details

Total release size: 14.3 kB

Release files / asyncrunner_executor-0.1.tar.gz

Download URL asyncrunner_executor-0.1.tar.gz
Size 7.2 kB
Tags Source
SHA-256 checksum
How to use checksums
ffe15170967bd80a3beaebb10b81f286e3fe084059a36a0af4bde57ab40da583
BLAKE2b-256 checksum
How to use checksums
bab03af1ad5121789d87fd47b7f78aaf517027da253b08e289c045c7878292cb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Release files / asyncrunner_executor-0.1-py3-none-any.whl

Download URL asyncrunner_executor-0.1-py3-none-any.whl
Size 7.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
229a398462a27acf33c4a8fdc57b4c60def78c514a67a96b3ca48e42869e1db5
BLAKE2b-256 checksum
How to use checksums
0d320da1e237737b3166445aa01e6e4f96c498f1478dbd0d0335c2cde7b1cf6f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.4

Security for asyncrunner-executor 0.1

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.

Release history Release notifications | RSS feed

This release

0.1 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