Skip to main content

Simple decorator for executing an asynchronous Python callable in a synchronous context.

Project description

syncable

syncable is a Python decorator that allows you to call asynchronous functions from both synchronous and asynchronous contexts seamlessly. This is especially useful for library authors and application developers who want to provide a unified API, regardless of whether the caller is in an async or sync context.


Features

  • Call async functions from sync code without worrying about event loops.
  • Works transparently in async, sync, and AnyIO worker thread contexts.
  • Preserves context variables (contextvars) across thread boundaries.
  • Simple, lightweight, and dependency-minimal (only requires anyio).

Installation

pip install syncable

Usage

Basic Example

Suppose you have an async function:

import asyncio

async def fetch_data():
    await asyncio.sleep(1)
    return "data"

With syncable, you can decorate it:

from syncable import syncable

@syncable
async def fetch_data():
    await asyncio.sleep(1)
    return "data"

Now you can call fetch_data() from both sync and async code:

From Synchronous Code

result = fetch_data()
print(result)  # "data"

From Asynchronous Code

async def main():
    result = await fetch_data()
    print(result)  # "data"

import asyncio
asyncio.run(main())

How It Works

  • Async context: Returns the coroutine for you to await.
  • Sync context: Runs the coroutine in a background event loop and blocks until the result is ready.
  • AnyIO worker thread: Uses AnyIO's thread portal to run the coroutine in the main event loop.

Advanced Example: Preserving Context Variables

import contextvars
from syncable import syncable

user_var = contextvars.ContextVar("user")

@syncable
async def whoami():
    return user_var.get()

def sync_caller():
    user_var.set("alice")
    print(whoami())  # prints "alice"

import asyncio

async def async_caller():
    user_var.set("bob")
    print(await whoami())  # prints "bob"

sync_caller()
asyncio.run(async_caller())

Accessing the Original Async Function

If you need the undecorated async function, use the .aio attribute:

@syncable
async def foo(): ...

foo.aio  # This is the original async function

Limitations

  • Async generators are not supported and will raise an error if decorated.
  • Relies on AnyIO internals for worker thread detection (may break with future AnyIO versions).
  • In rare edge cases (e.g., nested event loops in Jupyter), behavior may vary.

Logging

syncable uses Python's standard logging. To see debug logs, configure logging in your application:

import logging
logging.basicConfig(level=logging.DEBUG)

License

MIT


Contributing

Pull requests and issues are welcome!

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

syncable-0.2.0.tar.gz (5.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

syncable-0.2.0-py3-none-any.whl (5.5 kB view details)

Uploaded Python 3

File details

Details for the file syncable-0.2.0.tar.gz.

File metadata

  • Download URL: syncable-0.2.0.tar.gz
  • Upload date:
  • Size: 5.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for syncable-0.2.0.tar.gz
Algorithm Hash digest
SHA256 b0b1fe217a594bdcaa0d25990e979eb1e4c35c99a3c21608dc8cddc86b3a3ee9
MD5 09b390e393418966fbc84218bedf2f12
BLAKE2b-256 143ab5e24809e377955ff423d7f983f75d26a59f0e9b45ad23b24e322587d0ad

See more details on using hashes here.

File details

Details for the file syncable-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: syncable-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 5.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.5

File hashes

Hashes for syncable-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 04aaad44b92573be08aae3b0d4f4abb506c7c8a7a8f30e26bae06f88afb805ab
MD5 db875c5f0a370dc7d21824fd5d9d925d
BLAKE2b-256 9de47a007566c3a3516f94140384e408a7cdf90e8cfeca507720ee9141571692

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page