🔍 Code Extractor

function add_sync

Maturity: 34

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.

File:
/tf/active/vicechatdev/rmcl/sync.py
Lines:
10 - 20
Complexity:
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

  • trio
  • functools
  • inspect

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

    A decorator that implements reentrant locking for async methods, allowing the same task to acquire the lock multiple times without deadlocking.

    From: /tf/active/vicechatdev/rmcl/items.py
  • function async_action 56.1% similar

    A decorator function that marks another function as asynchronous by adding an 'is_async' attribute, while preserving the original function's metadata.

    From: /tf/active/vicechatdev/CDocs/controllers/__init__.py
  • function async_execute 48.4% similar

    Wraps and schedules async function execution in the appropriate event loop context, ensuring proper lock propagation and document context management for Bokeh/Panel applications.

    From: /tf/active/vicechatdev/patches/server.py
  • function transaction 45.2% similar

    A decorator function that wraps another function to provide database transaction management capabilities, currently implemented as a placeholder for future transaction handling.

    From: /tf/active/vicechatdev/CDocs/controllers/__init__.py
  • function with_lock 44.9% similar

    A decorator that wraps callback functions (both sync and async) to mark them for execution with a Bokeh document lock, allowing safe modification of Bokeh models.

    From: /tf/active/vicechatdev/patches/server.py
← Back to Browse