if runner is not None:
return runner.handle_exception(typ, value, tb)
return False
- with ExceptionStackContext(handle_exception):
+ with ExceptionStackContext(handle_exception) as deactivate:
gen = func(*args, **kwargs)
if isinstance(gen, types.GeneratorType):
- runner = Runner(gen)
+ runner = Runner(gen, deactivate)
runner.run()
return
assert gen is None, gen
+ deactivate()
# no yield, so we're done
return wrapper
Maintains information about pending callbacks and their results.
"""
- def __init__(self, gen):
+ def __init__(self, gen, deactivate_stack_context):
self.gen = gen
+ self.deactivate_stack_context = deactivate_stack_context
self.yield_point = _NullYieldPoint()
self.pending_callbacks = set()
self.results = {}
raise LeakedCallbackError(
"finished without waiting for callbacks %r" %
self.pending_callbacks)
+ self.deactivate_stack_context()
return
except Exception:
self.finished = True
import contextlib
import functools
import itertools
+import operator
import sys
import threading
with StackContext(my_context):
'''
- def __init__(self, context_factory):
+ def __init__(self, context_factory, _active_cell=None):
self.context_factory = context_factory
+ self.active_cell = _active_cell or [True]
# Note that some of this code is duplicated in ExceptionStackContext
# below. ExceptionStackContext is more common and doesn't need
# the full generality of this class.
def __enter__(self):
self.old_contexts = _state.contexts
- # _state.contexts is a tuple of (class, arg) pairs
+ # _state.contexts is a tuple of (class, arg, active_cell) tuples
_state.contexts = (self.old_contexts +
- ((StackContext, self.context_factory),))
+ ((StackContext, self.context_factory, self.active_cell),))
try:
self.context = self.context_factory()
self.context.__enter__()
except Exception:
_state.contexts = self.old_contexts
raise
+ return lambda: operator.setitem(self.active_cell, 0, False)
def __exit__(self, type, value, traceback):
try:
If the exception handler returns true, the exception will be
consumed and will not be propagated to other exception handlers.
'''
- def __init__(self, exception_handler):
+ def __init__(self, exception_handler, _active_cell=None):
self.exception_handler = exception_handler
+ self.active_cell = _active_cell or [True]
def __enter__(self):
self.old_contexts = _state.contexts
_state.contexts = (self.old_contexts +
- ((ExceptionStackContext, self.exception_handler),))
+ ((ExceptionStackContext, self.exception_handler,
+ self.active_cell),))
+ return lambda: operator.setitem(self.active_cell, 0, False)
def __exit__(self, type, value, traceback):
try:
callback(*args, **kwargs)
return
if not _state.contexts:
- new_contexts = [cls(arg) for (cls, arg) in contexts]
+ new_contexts = [cls(arg, active_cell)
+ for (cls, arg, active_cell) in contexts
+ if active_cell[0]]
# If we're moving down the stack, _state.contexts is a prefix
# of contexts. For each element of contexts not in that prefix,
# create a new StackContext object.
for a, b in itertools.izip(_state.contexts, contexts))):
# contexts have been removed or changed, so start over
new_contexts = ([NullContext()] +
- [cls(arg) for (cls, arg) in contexts])
+ [cls(arg, active_cell)
+ for (cls, arg, active_cell) in contexts
+ if active_cell[0]])
else:
- new_contexts = [cls(arg)
- for (cls, arg) in contexts[len(_state.contexts):]]
+ new_contexts = [cls(arg, active_cell)
+ for (cls, arg, active_cell) in contexts[len(_state.contexts):]
+ if active_cell[0]]
if len(new_contexts) > 1:
with _nested(*new_contexts):
callback(*args, **kwargs)
library_function(final_callback)
self.wait()
+ def test_deactivate(self):
+ deactivate_callbacks = []
+ def f1():
+ with StackContext(functools.partial(self.context, 'c1')) as c1:
+ deactivate_callbacks.append(c1)
+ self.io_loop.add_callback(f2)
+ def f2():
+ with StackContext(functools.partial(self.context, 'c2')) as c2:
+ deactivate_callbacks.append(c2)
+ self.io_loop.add_callback(f3)
+ def f3():
+ with StackContext(functools.partial(self.context, 'c3')) as c3:
+ deactivate_callbacks.append(c3)
+ self.io_loop.add_callback(f4)
+ def f4():
+ self.assertEqual(self.active_contexts, ['c1', 'c2', 'c3'])
+ deactivate_callbacks[1]()
+ # deactivating a context doesn't remove it immediately,
+ # but it will be missing from the next iteration
+ self.assertEqual(self.active_contexts, ['c1', 'c2', 'c3'])
+ self.io_loop.add_callback(f5)
+ def f5():
+ self.assertEqual(self.active_contexts, ['c1', 'c3'])
+ self.stop()
+ self.io_loop.add_callback(f1)
+ self.wait()
+
if __name__ == '__main__':
unittest.main()