Close #15153: Added inspect.getgeneratorlocals to simplify whitebox testing of generator state updates

This commit is contained in:
Nick Coghlan 2012-06-23 19:52:05 +10:00
parent 766e62266e
commit 04e2e3f231
5 changed files with 98 additions and 0 deletions

View File

@ -676,3 +676,27 @@ generator to be determined easily.
* GEN_CLOSED: Execution has completed.
.. versionadded:: 3.2
The current internal state of the generator can also be queried. This is
mostly useful for testing purposes, to ensure that internal state is being
updated as expected:
.. function:: getgeneratorlocals(generator)
Get the mapping of live local variables in *generator* to their current
values. A dictionary is returned that maps from variable names to values.
This is the equivalent of calling :func:`locals` in the body of the
generator, and all the same caveats apply.
If *generator* is a :term:`generator` with no currently associated frame,
then an empty dictionary is returned. :exc:`TypeError` is raised if
*generator* is not a Python generator object.
.. impl-detail::
This function relies on the generator exposing a Python stack frame
for introspection, which isn't guaranteed to be the case in all
implementations of Python. In such cases, this function will always
return an empty dictionary.
.. versionadded:: 3.3

View File

@ -1037,6 +1037,13 @@ state when testing code that relies on stateful closures.
(Contributed by Meador Inge and Nick Coghlan in :issue:`13062`)
A new :func:`~inspect.getgeneratorlocals` function has been added. This
function reports the current binding of local variables in the generator's
stack frame, making it easier to verify correct internal state when testing
generators.
(Contributed by Meador Inge in :issue:`15153`)
io
--

View File

@ -1259,6 +1259,8 @@ def getattr_static(obj, attr, default=_sentinel):
raise AttributeError(attr)
# ------------------------------------------------ generator introspection
GEN_CREATED = 'GEN_CREATED'
GEN_RUNNING = 'GEN_RUNNING'
GEN_SUSPENDED = 'GEN_SUSPENDED'
@ -1282,6 +1284,22 @@ def getgeneratorstate(generator):
return GEN_SUSPENDED
def getgeneratorlocals(generator):
"""
Get the mapping of generator local variables to their current values.
A dict is returned, with the keys the local variable names and values the
bound values."""
if not isgenerator(generator):
raise TypeError("'{!r}' is not a Python generator".format(generator))
frame = getattr(generator, "gi_frame", None)
if frame is not None:
return generator.gi_frame.f_locals
else:
return {}
###############################################################################
### Function Signature Object (PEP 362)
###############################################################################

View File

@ -1271,6 +1271,52 @@ class TestGetGeneratorState(unittest.TestCase):
self.assertIn(name, repr(state))
self.assertIn(name, str(state))
def test_getgeneratorlocals(self):
def each(lst, a=None):
b=(1, 2, 3)
for v in lst:
if v == 3:
c = 12
yield v
numbers = each([1, 2, 3])
self.assertEqual(inspect.getgeneratorlocals(numbers),
{'a': None, 'lst': [1, 2, 3]})
next(numbers)
self.assertEqual(inspect.getgeneratorlocals(numbers),
{'a': None, 'lst': [1, 2, 3], 'v': 1,
'b': (1, 2, 3)})
next(numbers)
self.assertEqual(inspect.getgeneratorlocals(numbers),
{'a': None, 'lst': [1, 2, 3], 'v': 2,
'b': (1, 2, 3)})
next(numbers)
self.assertEqual(inspect.getgeneratorlocals(numbers),
{'a': None, 'lst': [1, 2, 3], 'v': 3,
'b': (1, 2, 3), 'c': 12})
try:
next(numbers)
except StopIteration:
pass
self.assertEqual(inspect.getgeneratorlocals(numbers), {})
def test_getgeneratorlocals_empty(self):
def yield_one():
yield 1
one = yield_one()
self.assertEqual(inspect.getgeneratorlocals(one), {})
try:
next(one)
except StopIteration:
pass
self.assertEqual(inspect.getgeneratorlocals(one), {})
def test_getgeneratorlocals_error(self):
self.assertRaises(TypeError, inspect.getgeneratorlocals, 1)
self.assertRaises(TypeError, inspect.getgeneratorlocals, lambda x: True)
self.assertRaises(TypeError, inspect.getgeneratorlocals, set)
self.assertRaises(TypeError, inspect.getgeneratorlocals, (2,3))
class TestSignatureObject(unittest.TestCase):
@staticmethod

View File

@ -40,6 +40,9 @@ Core and Builtins
Library
-------
- Issue #15153: Added inspect.getgeneratorlocals to simplify white box
testing of generator state updates
- Issue #13062: Added inspect.getclosurevars to simplify testing stateful
closures