Move generator context state to a generator context manager

This commit is contained in:
Jean-Paul Calderone 2019-02-22 08:40:16 -05:00
parent d902f7567f
commit e55ee7d044

View File

@ -5,6 +5,8 @@ Tools aimed at the interaction between Tahoe-LAFS implementation and Eliot.
from sys import exc_info from sys import exc_info
from functools import wraps from functools import wraps
from contextlib import contextmanager from contextlib import contextmanager
from weakref import WeakKeyDictionary
from eliot import ( from eliot import (
Message, Message,
@ -14,25 +16,44 @@ from twisted.internet.defer import (
inlineCallbacks, inlineCallbacks,
) )
@contextmanager class _GeneratorContext(object):
def _substitute_stack(substitute, target): def __init__(self, execution_context):
# Save whatever is there to begin with, making a copy ensures we don't get self._execution_context = execution_context
# affected by any mutations that might happen while the substitute is in self._contexts = WeakKeyDictionary()
# place. self._current_generator = None
saved = list(target)
# Put the substitute in place. Preserve the identity of the target for no def init_stack(self, generator):
# concrete reason but maybe it's a good idea. stack = list(self._execution_context._get_stack())
target[:] = substitute self._contexts[generator] = stack
try:
# Let some code run. def get_stack(self):
yield if self._current_generator is None:
finally: # If there is no currently active generator then we have no
# Save whatever substitute state we ended up with back to the # special stack to supply. Let the execution context figure out a
# substitute. Copying again, here. # different answer on its own.
substitute[:] = list(target) return None
# Restore the target to its original state. Again, preserving # Otherwise, give back the action context stack we've been tracking
# identity. # for the currently active generator. It must have been previously
target[:] = saved # initialized (it's too late to do it now)!
return self._contexts[self._current_generator]
@contextmanager
def context(self, generator):
previous_generator = self._current_generator
try:
self._current_generator = generator
yield
finally:
self._current_generator = previous_generator
from eliot._action import _context
_the_generator_context = _GeneratorContext(_context)
def use_generator_context():
_context.get_sub_context = _the_generator_context.get_stack
use_generator_context()
def eliot_friendly_generator_function(original): def eliot_friendly_generator_function(original):
@ -49,16 +70,17 @@ def eliot_friendly_generator_function(original):
# Keep track of the next value to deliver to the generator. # Keep track of the next value to deliver to the generator.
value_in = None value_in = None
# Start tracking our desired inward-facing action context stack. This
# really wants some more help from Eliot.
from eliot._action import _context
context_in = list(_context._get_stack())
# Create the generator with a call to the generator function. This # Create the generator with a call to the generator function. This
# happens with whatever Eliot action context happens to be active, # happens with whatever Eliot action context happens to be active,
# which is fine and correct and also irrelevant because no code in the # which is fine and correct and also irrelevant because no code in the
# generator function can run until we call send or throw on it. # generator function can run until we call send or throw on it.
gen = original(*a, **kw) gen = original(*a, **kw)
# Initialize the per-generator Eliot action context stack to the
# current action stack. This might be the main stack or, if another
# decorated generator is running, it might be the stack for that
# generator. Not our business.
_the_generator_context.init_stack(gen)
try: try:
while True: while True:
try: try:
@ -66,7 +88,7 @@ def eliot_friendly_generator_function(original):
# with the Eliot action context stack we've saved for it. # with the Eliot action context stack we've saved for it.
# Then the context manager will re-save it and restore the # Then the context manager will re-save it and restore the
# "outside" stack for us. # "outside" stack for us.
with _substitute_stack(context_in, _context._get_stack()): with _the_generator_context.context(gen):
if ok: if ok:
value_out = gen.send(value_in) value_out = gen.send(value_in)
else: else:
@ -105,6 +127,7 @@ def eliot_friendly_generator_function(original):
return wrapper return wrapper
def inline_callbacks(original): def inline_callbacks(original):
""" """
Decorate a function like ``inlineCallbacks`` would but in a more Decorate a function like ``inlineCallbacks`` would but in a more