function add_sync
A decorator that creates a synchronous wrapper function for an async function, automatically running it with trio.run() and injecting it into the caller's namespace with a suffix.
/tf/active/vicechatdev/rmcl/sync.py
10 - 20
moderate
Purpose
This decorator is designed to automatically generate synchronous versions of async functions in the Trio async framework. It wraps an async function, creates a synchronous counterpart that uses trio.run() to execute the async code, and injects this new function into the caller's local namespace. The synchronous version has the same name as the original with a SUFFIX appended (SUFFIX must be defined elsewhere in the codebase). This is useful for providing both async and sync APIs from a single function definition.
Source Code
def add_sync(afunc):
@functools.wraps(afunc, assigned=('__doc__', '__annotations__'))
def sfunc(*args, **kw):
async def runner():
return await afunc(*args, **kw)
return trio.run(runner)
sfunc.__name__ = afunc.__name__ + SUFFIX
sfunc.__qualname__ = afunc.__qualname__ + SUFFIX
inspect.currentframe().f_back.f_locals[sfunc.__name__] = sfunc
return afunc
Parameters
| Name | Type | Default | Kind |
|---|---|---|---|
afunc |
- | - | positional_or_keyword |
Parameter Details
afunc: An async function (coroutine function) that will be wrapped. This should be a function defined with 'async def'. The decorator will create a synchronous version of this function that can be called without await.
Return Value
Returns the original async function (afunc) unchanged. As a side effect, it creates and injects a synchronous wrapper function into the caller's namespace with the name '{original_name}{SUFFIX}'. The synchronous wrapper returns whatever the async function returns when executed.
Dependencies
triofunctoolsinspect
Required Imports
import functools
import inspect
import trio
Usage Example
import functools
import inspect
import trio
SUFFIX = '_sync'
def add_sync(afunc):
@functools.wraps(afunc, assigned=('__doc__', '__annotations__'))
def sfunc(*args, **kw):
async def runner():
return await afunc(*args, **kw)
return trio.run(runner)
sfunc.__name__ = afunc.__name__ + SUFFIX
sfunc.__qualname__ = afunc.__qualname__ + SUFFIX
inspect.currentframe().f_back.f_locals[sfunc.__name__] = sfunc
return afunc
@add_sync
async def fetch_data(url):
await trio.sleep(1)
return f"Data from {url}"
# Now both async and sync versions exist
# Async version: await fetch_data('http://example.com')
# Sync version: fetch_data_sync('http://example.com')
result = fetch_data_sync('http://example.com')
print(result)
Best Practices
- Ensure SUFFIX is defined as a module-level constant before using this decorator
- This decorator modifies the caller's namespace directly using frame inspection, which can be fragile and may not work in all contexts (e.g., interactive shells, certain execution environments)
- The decorator is specifically designed for Trio async framework and won't work with asyncio
- Be aware that the synchronous wrapper blocks the entire thread while the async function runs, so it's not suitable for concurrent operations
- The decorator returns the original async function, so the async version remains available for use
- Consider documenting both the async and sync versions in your API documentation
- Avoid using this pattern in performance-critical code where blocking the thread is undesirable
Similar Components
AI-powered semantic similarity - components with related functionality:
-
function with_lock_v1 62.4% similar
-
function async_action 56.1% similar
-
function async_execute 48.4% similar
-
function transaction 45.2% similar
-
function with_lock 44.9% similar