def wrapper(*args, **kwargs): future = TracebackFuture() # ipdb.set_trace() print args, kwargs # callback, args, kwargs = replacer.replace(lambda value=_NO_RESULT: future.set_result(value),args, kwargs) callback = None kwargs['callback'] = lambda value = _NO_RESULT: future.set_result(value) print callback, args, kwargs def handle_error(typ, value, tb): future.set_exc_info((typ, value, tb)) return True exc_info = None with ExceptionStackContext(handle_error): try: result = f(*args, **kwargs) if result is not None: raise ReturnValueIgnoredError('@return_future should not be used with function that return values') except: exc_info = sys.exc_info() raise if exc_info is not None: future.result() if callback is not None: def run_callback(future): result = future.result() if result is _NO_RESULT: callback('result is NULL') else: callback(future.result()) future.add_done_callback(wrap(run_callback)) # print future.result(), 'in the wapper' return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception(typ, value, tb): return True except Exception: typ, value, tb = sys.exc_info() future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception): try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): future.set_result(value) runner = Runner(result, final_callback) runner.run() return future future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: # 调用的参数有callback,不实用future使用异步callback callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: # 调用实际的处理函数如果返回GeneratorType,那么进入Runner类循环执行直到终止 result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future.set_exc_info(sys.exc_info()) return future else: # 生成器 if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts # 返回第一个Future,异步调用返回的Future, # 在异步调用成功后会将这个Future给set_done yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future.set_result(_value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: Runner(result, future, yielded) try: # 进入Runner,返回 return future finally: # Subtle memory optimization: if next() raised an exception, # the future's exc_info contains a traceback which # includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future.set_result(result) return future
def async (task, *args, **kwargs): future = TracebackFuture() callback = kwargs.pop("callback", None) if callback: IOLoop.instance().add_future(future, lambda future: callback(future.result())) result = task.delay(*args, **kwargs) IOLoop.instance().add_callback(_on_result, result, future) return future
def wrapper(*args, **kwargs): ipdb.set_trace() future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future(future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: # ipdb.set_trace() result = gen._value_from_stopiteration(e) except Exception: # ipdb.set_trace() future.set_exc_info(sys.exc_info()) return future else: # ipdb.set_trace() if isinstance(result, GeneratorType): try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception(stack_context.StackContextInconsistentError('stack_context inconsistency (probably caused')) except (StopIteration, Return) as e: future.set_result(gen._value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: try: result.send(yielded.result()) except (StopIteration, Return) as e: ipdb.set_trace() future.set_result(gen._value_from_stopiteration(e)) # gen.Runner(result, future, yielded) try: return future finally: future = None future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and "callback" in kwargs: callback = kwargs.pop("callback") IOLoop.current().add_future(future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( "stack_context inconsistency (probably caused " 'by yield within a "with StackContext" block)' ) ) except (StopIteration, Return) as e: future.set_result(_value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: _futures_to_runners[future] = Runner(result, future, yielded) try: return future finally: # Subtle memory optimization: if next() raised an exception, # the future's exc_info contains a traceback which # includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future.set_result(result) return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() # 注册进去了...... if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception( typ, value, tb): return True # 这样就吃掉exception了? except Exception: typ, value, tb = sys.exc_info() future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) # result是func调用返回的一个结果,有可能是一个iterator? except (Return, StopIteration) as e: result = getattr(e, 'value', None) #TODO 这里也是 except Exception: deactivate() future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): deactivate() future.set_result(value) # 这里的result很可能是一个iterator,看看头部的调用例子 runner = Runner(result, final_callback) # 关键点 runner.run() return future deactivate() future.set_result(result) return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() # 注册进去了...... if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception(typ, value, tb): return True # 这样就吃掉exception了? except Exception: typ, value, tb = sys.exc_info() future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) # result是func调用返回的一个结果,有可能是一个iterator,比如我们的get、post方法里面塞几个yield的调用 except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: deactivate() future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): deactivate() future.set_result(value) # 这里的result很可能是一个iterator,看看头部的调用例子 runner = Runner(result, final_callback) # 关键点 runner.run() return future deactivate() future.set_result(result) return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception(typ, value, tb): return True except Exception: typ, value, tb = sys.exc_info() if sys.getrefcount(future) == 2: # there are no references left to this future object # to prevent the error passing silently throw the exception right here # so it can be catched and handled by IOLoop.handle_callback_exception raise else: future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: deactivate() future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): deactivate() future.set_result(value) runner = Runner(result, final_callback) runner.run() return future deactivate() future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: with Measure(func): result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): try: orig_stack_contexts = stack_context._state.contexts with Measure(result, first=True): yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)')) except (StopIteration, Return) as e: future.set_result(getattr(e, 'value', None)) except Exception: future.set_exc_info(sys.exc_info()) else: Runner(result, future, yielded) try: return future finally: future = None future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and "callback" in kwargs: callback = kwargs.pop("callback") IOLoop.current().add_future(future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, "value", None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( "stack_context inconsistency (probably caused " 'by yield within a "with StackContext" block)' ) ) except (StopIteration, Return) as e: future.set_result(getattr(e, "value", None)) except Exception: future.set_exc_info(sys.exc_info()) else: Runner(result, future, yielded) return future future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future.set_result(getattr(e, 'value', None)) except Exception: future.set_exc_info(sys.exc_info()) else: Runner(result, future, yielded) return future future.set_result(result) return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() if 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) def handle_exception(typ, value, tb): try: if runner is not None and runner.handle_exception( typ, value, tb): return True except Exception: typ, value, tb = sys.exc_info() future.set_exc_info((typ, value, tb)) return True with ExceptionStackContext(handle_exception) as deactivate: try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: deactivate() future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): def final_callback(value): deactivate() future.set_result(value) runner = Runner(result, final_callback) runner.run() return future deactivate() future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future.set_result(getattr(e, 'value', None)) except Exception: future.set_exc_info(sys.exc_info()) else: Runner(result, future, yielded) try: return future finally: future = None future.set_result(result) return future
def wrapper(*args, **kwargs): runner = None future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = getattr(e, 'value', None) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, types.GeneratorType): runner = Runner(result, future) runner.run() return future future.set_result(result) return future
class AsyncResult(object): """ Essentially a wrapper for a Tornado TracebackFuture """ def __init__(self, future=None): if future is not None: self._future = future else: self._future = TracebackFuture() self._future.add_done_callback(functools.partial(async_result_complete, self)) self._condition = threading.Condition() def ready(self): """Return `True` if and only if it holds a value or an exception""" return self._future.done() def successful(self): """Return `True` if and only if it is ready and holds a value""" return self._future.exception() is None @property def exception(self): return self._future.exception() def set(self, value=None): """Store the value. Wake up the waiters. :param value: Value to store as the result. Any waiters blocking on :meth:`get` or :meth:`wait` are woken up. Sequential calls to :meth:`wait` and :meth:`get` will not block at all.""" with self._condition: self._future.set_result(value) def set_exception(self, exception): """Store the exception. Wake up the waiters. :param exception: Exception to raise when fetching the value. Any waiters blocking on :meth:`get` or :meth:`wait` are woken up. Sequential calls to :meth:`wait` and :meth:`get` will not block at all.""" with self._condition: self._future.set_exception(exception) def get(self, block=True, timeout=None): """Return the stored value or raise the exception :param block: Whether this method should block or return immediately. :type block: bool :param timeout: How long to wait for a value when `block` is `True`. :type timeout: float If this instance already holds a value / an exception, return / raise it immediately. Otherwise, block until :meth:`set` or :meth:`set_exception` has been called or until the optional timeout occurs.""" with self._condition: if self.ready(): return self._future.result() elif block: self._condition.wait(timeout) return self._future.result() # if we get to this point we timeout raise KazooTimeoutError() def get_nowait(self): """Return the value or raise the exception without blocking. If nothing is available, raise the Timeout exception class on the associated :class:`IHandler` interface.""" return self._future.result() def wait(self, timeout=None): """Block until the instance is ready. :param timeout: How long to wait for a value :type timeout: float If this instance already holds a value / an exception, return / raise it immediately. Otherwise, block until :meth:`set` or :meth:`set_exception` has been called or until the optional timeout occurs.""" with self._condition: self._condition.wait(timeout) return self.ready() def rawlink(self, callback): """Register a callback to call when a value or an exception is set :param callback: A callback function to call after :meth:`set` or :meth:`set_exception` has been called. This function will be passed a single argument, this instance. :type callback: func """ def on_callback(future): callback(AsyncResult(future)) IOLoop.current().add_future(self._future, on_callback) def unlink(self, callback): """Remove the callback set by :meth:`rawlink` :param callback: A callback function to remove. :type callback: func """ def notify(self): self._condition.notify_all()
def wrapper(*args, **kwargs): # 这个 future 对象是用来存储 coroutine 执行完毕后的结果的。结果通常有 # 两种来源,一个是在 coroutine 中 yield Return 对象,另一种则是 # coroutine 作为 generator 执行完毕后 raise # StopIteration,并有结果数据存储在 StopIteration 这个异常对象中。 future = TracebackFuture() if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') IOLoop.current().add_future( future, lambda future: callback(future.result())) try: result = func(*args, **kwargs) except (Return, StopIteration) as e: result = _value_from_stopiteration(e) except Exception: future.set_exc_info(sys.exc_info()) return future else: if isinstance(result, GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts yielded = next(result) if stack_context._state.contexts is \ not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)') ) except (StopIteration, Return) as e: future.set_result(_value_from_stopiteration(e)) except Exception: future.set_exc_info(sys.exc_info()) else: # Runner 负责执行一个 coroutine,如果 coroutine 中的异步 # 操作已经完成,则 future 会被填充该操作的结果,否则 future # 会继续处于等待结果的状态,并紧接着在下一行代码返回给 # coroutine 的调用者。 Runner(result, future, yielded) try: return future finally: # Subtle memory optimization: if next() raised an # exception, the future's exc_info contains a traceback # which includes this stack frame. This creates a cycle, # which will be collected at the next full GC but has # been shown to greatly increase memory usage of # benchmarks (relative to the refcount-based scheme # used in the absence of cycles). We can avoid the # cycle by clearing the local variable after we return it. future = None future.set_result(result) return future
def wrapper(*args, **kwargs): future = TracebackFuture() # 一个future的跟踪对象,这个是关键 # 原来的func不支持callback的参数,修饰之后是可以使用的 # 下面的这个future好好读懂,然后好好理解传说中的future功能 if replace_callback and 'callback' in kwargs: callback = kwargs.pop('callback') # 就是future成功了,执行lambda future: callback(future.result())函数,参数就是这个future # 也就是把这个可能执行的语句放到IOLoop里面去,这个就是这个add_future的功能 IOLoop.current().add_future( future, lambda future: callback(future.result())) # 异步的HTTP和异步的IOStream也许返回的类型不一样, # 感觉两个异步的东西才是tornado的关键 try: # 尝试运行函数,这里会yield出来,所以不会被阻塞的 # 1. 返回一个Return 2. 返回一个StopIteration(这个不是很清楚??) # 3. 返回一个Exception?就是运行错误了 result = func(*args, **kwargs) except (Return, StopIteration) as e: # 到这里说明结果已经出来了,future的任务结束了 # ?StopIteration无法理解 result = getattr(e, 'value', None) except Exception: # 如果出错的话 # 运行出错了,这个时候依旧会添加到IOLoop._callbacks # 下面这句话执行这个future全部的回调 future.set_exc_info(sys.exc_info()) return future # 一但结果确定,直接返回一个future else: # 如果内部有yield,直接返回一个types.GeneratorType if isinstance(result, types.GeneratorType): # Inline the first iteration of Runner.run. This lets us # avoid the cost of creating a Runner when the coroutine # never actually yields, which in turn allows us to # use "optional" coroutines in critical path code without # performance penalty for the synchronous case. try: orig_stack_contexts = stack_context._state.contexts # 这里是获取yield的结果的地方,一般还是一个future的东西 yielded = next(result) # 获取yield的结果,这里一般是一个future类型yield出来的东西 # 先忽略下面的出错处理 if stack_context._state.contexts is not orig_stack_contexts: yielded = TracebackFuture() yielded.set_exception( stack_context.StackContextInconsistentError( 'stack_context inconsistency (probably caused ' 'by yield within a "with StackContext" block)')) except (StopIteration, Return) as e: # 直接出结果 future.set_result(getattr(e, 'value', None)) except Exception: # bug了 future.set_exc_info(sys.exc_info()) else: # 返回一个future Runner(result, future, yielded) # 如果是一个Exception,这里记录到future,然后返回了 return future future.set_result(result) # 这个时候添加回调到了IOLoop return future