def run_sync(f, *args, **kwargs): import crochet timeout = kwargs.pop('timeout', None) @wraps(f) def wrapped(): return f(*args, **kwargs) if not run_sync._is_setup: crochet.setup() run_sync._is_setup = True return crochet.run_in_reactor(wrapped)().wait(timeout)
def asynchronous(func=undefined, *, timeout=undefined): """Decorates a function to ensure that it always runs in the reactor. If the wrapper is called from the reactor thread, it will call straight through to the wrapped function. It will not be wrapped by `maybeDeferred` for example. If the wrapper is called from another thread, it will return a :py::class:`crochet.EventualResult`, as if it had been decorated with `crochet.run_in_reactor`. There's an additional convenience. If `timeout` has been specified, the :py:class:`~crochet.EventualResult` will be waited on for up to `timeout` seconds. This means that callers don't need to remember to wait. If `timeout` is `FOREVER` then it will wait indefinitely, which can be useful where the function itself handles time-outs, or where the called function doesn't actually defer work but just needs to run in the reactor thread. It is possible to programmatically determine if a function has been thusly decorated by checking if `IAsynchronous` is provided:: if IAsynchronous.providedBy(a_function): ... # a_function has been decorated with @asynchronous This also serves a secondary documentation purpose; functions decorated with this are readily identifiable as asynchronous. """ if func is undefined: return partial(asynchronous, timeout=timeout) if timeout is not undefined: if isinstance(timeout, (int, float)): if timeout < 0: raise ValueError("timeout must be >= 0, not %d" % timeout) elif timeout is not FOREVER: raise ValueError( "timeout must an int, float, or undefined, not %r" % (timeout, )) func_in_reactor = run_in_reactor(func) @wraps(func) def wrapper(*args, **kwargs): if threadable.ioThread is None or isInIOThread(): return func(*args, **kwargs) elif timeout is undefined: return func_in_reactor(*args, **kwargs) elif timeout is FOREVER: # There's a bug in crochet where waiting for an undefined amount # of time -- i.e. by calling .wait() or .wait(None) -- waits for # an invalidly long time (2^31 seconds) which causes NO wait; i.e. # TimeoutError is raised immediately. Instead we wait 4 weeks, # which seems long enough, even for MAAS. return func_in_reactor(*args, **kwargs).wait(LONGTIME) else: return func_in_reactor(*args, **kwargs).wait(timeout) # This makes it possible to reliably determine programmatically if a # function has been decorated with @asynchronous. interface.directlyProvides(wrapper, IAsynchronous) return wrapper