async def test_create_future_for_given_loop(self, mocker, event_loop): spy_create_future = mocker.spy(event_loop, "create_future") result = asyncio_compat.create_future(event_loop) assert isinstance(result, asyncio.Future) assert result._loop == event_loop # Future.get_loop() only works in Python 3.7+ assert spy_create_future.call_count == 1 assert spy_create_future.call_args == mocker.call()
async def test_create_future_for_given_loop_py351orless_compat(self, mocker, event_loop): spy_future = mocker.spy(asyncio, "Future") result = asyncio_compat.create_future(event_loop) assert isinstance( result, spy_future.side_effect ) # spy_future.side_effect == asyncio.Future assert result._loop == event_loop # Future.get_loop() only works in Python 3.7+ assert spy_future.call_count == 1 assert spy_future.call_args == mocker.call(loop=event_loop)
def __init__(self, callback): """Creates an instance of an AwaitableCallback from a callback function. :param callback: Callback function to be made awaitable. """ loop = asyncio_compat.get_running_loop() self.future = asyncio_compat.create_future(loop) def wrapping_callback(*args, **kwargs): result = callback(*args, **kwargs) # Use event loop from outer scope, since the threads it will be used in will not have # an event loop. future.set_result() has to be called in an event loop or it does not work. loop.call_soon_threadsafe(self.future.set_result, result) return result self.callback = wrapping_callback
def __init__(self, return_arg_name=None): """Creates an instance of an AwaitableCallback """ # LBYL because this mistake doesn't cause an exception until the callback # which is much later and very difficult to trace back to here. if return_arg_name and not isinstance(return_arg_name, str): raise TypeError("internal error: return_arg_name must be a string") loop = asyncio_compat.get_running_loop() self.future = asyncio_compat.create_future(loop) def wrapping_callback(*args, **kwargs): # Use event loop from outer scope, since the threads it will be used in will not have # an event loop. future.set_result() and future.set_exception have to be called in an # event loop or they do not work. if "error" in kwargs and kwargs["error"]: exception = kwargs["error"] elif return_arg_name: if return_arg_name in kwargs: exception = None result = kwargs[return_arg_name] else: raise TypeError( "internal error: excepected argument with name '{}', did not get" .format(return_arg_name)) else: exception = None result = None if exception: # Do not use exc_info parameter on logger.error. This casuses pytest to save the traceback which saves stack frames which shows up as a leak logger.error( "Callback completed with error {}".format(exception)) logger.error( traceback.format_exception_only(type(exception), exception)) loop.call_soon_threadsafe(self.future.set_exception, exception) else: logger.debug( "Callback completed with result {}".format(result)) loop.call_soon_threadsafe(self.future.set_result, result) self.callback = wrapping_callback