Greenback
Greenback is a Python library that enables calling asynchronous code from a synchronous context within an active `asyncio` or `Trio` event loop. It bridges the gap between synchronous and asynchronous programming, allowing for gradual migration of codebases and interoperability with async-unaware libraries. The current version is 1.3.0, and it maintains a focused release cadence as a small, specialized utility built on `greenlet`.
Warnings
- gotcha Greenback works by manipulating the C stack via `greenlet`. Poorly-behaved C extension modules that violate Python's assumption that objects are fully heap-allocated might crash when used with `greenback`.
- gotcha Calling `greenback.await_()` inside Python finalizers (`__del__` methods), signal handlers, or weakref callbacks is explicitly unsupported and can lead to unexpected behavior or crashes.
- gotcha Setting up a `greenback` portal (e.g., via `await greenback.ensure_portal()`) incurs a minor performance overhead for the task it's enabled on, as each task step passes through `greenback`.
- gotcha The `greenback.with_portal_run_tree()` and other portal propagation functions designed for child tasks only work with the `Trio` event loop, not with `asyncio`, due to `asyncio`'s different task model and lack of necessary instrumentation features.
Install
-
pip install greenback
Imports
- greenback
import greenback
- ensure_portal
await greenback.ensure_portal()
- await_
greenback.await_(async_function())
- with_portal_run_sync
greenback.with_portal_run_sync(sync_function)
Quickstart
import asyncio
import greenback
# An async function we want to call from sync code
async def fetch_data(item_id):
print(f"[Async] Fetching data for {item_id}...")
await asyncio.sleep(0.1) # Simulate I/O
return f"Data for {item_id}"
# A synchronous function that needs to call an async function
def process_item(item_id):
print(f"[Sync] Processing item {item_id}")
# Call the async function from sync context using greenback.await_
data = greenback.await_(fetch_data(item_id))
print(f"[Sync] Received: {data}")
return data
# The main async entry point
async def main():
# Ensure a greenback portal is set up for this task
await greenback.ensure_portal()
print("Portal ensured.")
# Call the synchronous function that will use greenback
result = process_item("A123")
print(f"Main task received: {result}")
# Example using with_portal_run_sync
def another_sync_task():
print("[Sync 2] Starting another sync task.")
result2 = greenback.await_(fetch_data("B456"))
print(f"[Sync 2] Got: {result2}")
return result2
final_result = greenback.with_portal_run_sync(another_sync_task)
print(f"Main task got final result: {final_result}")
if __name__ == "__main__":
asyncio.run(main())