Issue #10220: Add inspect.getgeneratorstate(). Initial patch by Rodolpho Eckhardt
This commit is contained in:
parent
d3309df40b
commit
e0f04659cd
|
@ -620,3 +620,25 @@ code execution::
|
|||
# in which case the descriptor itself will
|
||||
# have to do
|
||||
pass
|
||||
|
||||
Current State of a Generator
|
||||
----------------------------
|
||||
|
||||
When implementing coroutine schedulers and for other advanced uses of
|
||||
generators, it is useful to determine whether a generator is currently
|
||||
executing, is waiting to start or resume or execution, or has already
|
||||
terminated. func:`getgeneratorstate` allows the current state of a
|
||||
generator to be determined easily.
|
||||
|
||||
.. function:: getgeneratorstate(generator)
|
||||
|
||||
Get current state of a generator-iterator.
|
||||
|
||||
Possible states are:
|
||||
GEN_CREATED: Waiting to start execution.
|
||||
GEN_RUNNING: Currently being executed by the interpreter.
|
||||
GEN_SUSPENDED: Currently suspended at a yield expression.
|
||||
GEN_CLOSED: Execution has completed.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -554,6 +554,14 @@ New, Improved, and Deprecated Modules
|
|||
(Contributed by R. David Murray, :issue:`10321`.)
|
||||
|
||||
|
||||
* The :mod:`inspect` module has a new function :func:`getgenatorstate`
|
||||
to easily identify the current state of a generator as one of
|
||||
``GEN_CREATED``, ``GEN_RUNNING``, ``GEN_SUSPENDED`` or ``GEN_CLOSED``.
|
||||
|
||||
(Contributed by Rodolpho Eckhardt and Nick Coghlan, :issue:`10220`.)
|
||||
|
||||
.. XXX: Mention inspect.getattr_static (Michael Foord)
|
||||
|
||||
Multi-threading
|
||||
===============
|
||||
|
||||
|
|
|
@ -1128,3 +1128,23 @@ def getattr_static(obj, attr, default=_sentinel):
|
|||
if default is not _sentinel:
|
||||
return default
|
||||
raise AttributeError(attr)
|
||||
|
||||
|
||||
GEN_CREATED, GEN_RUNNING, GEN_SUSPENDED, GEN_CLOSED = range(4)
|
||||
|
||||
def getgeneratorstate(generator):
|
||||
"""Get current state of a generator-iterator.
|
||||
|
||||
Possible states are:
|
||||
GEN_CREATED: Waiting to start execution.
|
||||
GEN_RUNNING: Currently being executed by the interpreter.
|
||||
GEN_SUSPENDED: Currently suspended at a yield expression.
|
||||
GEN_CLOSED: Execution has completed.
|
||||
"""
|
||||
if generator.gi_running:
|
||||
return GEN_RUNNING
|
||||
if generator.gi_frame is None:
|
||||
return GEN_CLOSED
|
||||
if generator.gi_frame.f_lasti == -1:
|
||||
return GEN_CREATED
|
||||
return GEN_SUSPENDED
|
||||
|
|
|
@ -887,12 +887,57 @@ class TestGetattrStatic(unittest.TestCase):
|
|||
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
|
||||
|
||||
|
||||
class TestGetGeneratorState(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
def number_generator():
|
||||
for number in range(5):
|
||||
yield number
|
||||
self.generator = number_generator()
|
||||
|
||||
def _generatorstate(self):
|
||||
return inspect.getgeneratorstate(self.generator)
|
||||
|
||||
def test_created(self):
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_CREATED)
|
||||
|
||||
def test_suspended(self):
|
||||
next(self.generator)
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_SUSPENDED)
|
||||
|
||||
def test_closed_after_exhaustion(self):
|
||||
for i in self.generator:
|
||||
pass
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
|
||||
|
||||
def test_closed_after_immediate_exception(self):
|
||||
with self.assertRaises(RuntimeError):
|
||||
self.generator.throw(RuntimeError)
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
|
||||
|
||||
def test_running(self):
|
||||
# As mentioned on issue #10220, checking for the RUNNING state only
|
||||
# makes sense inside the generator itself.
|
||||
# The following generator checks for this by using the closure's
|
||||
# reference to self and the generator state checking helper method
|
||||
def running_check_generator():
|
||||
for number in range(5):
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_RUNNING)
|
||||
yield number
|
||||
self.assertEqual(self._generatorstate(), inspect.GEN_RUNNING)
|
||||
self.generator = running_check_generator()
|
||||
# Running up to the first yield
|
||||
next(self.generator)
|
||||
# Running after the first yield
|
||||
next(self.generator)
|
||||
|
||||
|
||||
def test_main():
|
||||
run_unittest(
|
||||
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
|
||||
TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
|
||||
TestGetcallargsFunctions, TestGetcallargsMethods,
|
||||
TestGetcallargsUnboundMethods, TestGetattrStatic
|
||||
TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
Loading…
Reference in New Issue