Search code examples
pythontornadocoroutine

Puzzled about the coroutine of tornado


I am trying to figure out the flowing code in gen.py of the tornado.

    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) # A (mark)
                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)  # B
    return future

I have two functions.

@coroutine
def foo():
     #do something
     bar = yield bar()
     #do something else

@coroutine
def bar():
     #do something
     return [1,2,3,4]

Then, call the foo() function, the code run to the point marked A. Function bar() will be called and because bar() is not GeneratorType it will run like a normal function. Next, set future result and return future. This point that set result before return future is most confusing. After bar() return the future, foo() get this future, but the future has set_result done before it handled by io_loop.add_future.

Any help will be appreciated, thanks in advance.


Solution

  • I was so stupid for ignoring the code:

    if self.handle_yield(first_yielded): 
           self.run() 
    

    if the future is done, the value will be sent to the corresponding generator.