Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e0f0465

Browse files
committed
Issue #10220: Add inspect.getgeneratorstate(). Initial patch by Rodolpho Eckhardt
1 parent d3309df commit e0f0465

5 files changed

Lines changed: 99 additions & 1 deletion

File tree

Doc/library/inspect.rst

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -620,3 +620,25 @@ code execution::
620620
# in which case the descriptor itself will
621621
# have to do
622622
pass
623+
624+
Current State of a Generator
625+
----------------------------
626+
627+
When implementing coroutine schedulers and for other advanced uses of
628+
generators, it is useful to determine whether a generator is currently
629+
executing, is waiting to start or resume or execution, or has already
630+
terminated. func:`getgeneratorstate` allows the current state of a
631+
generator to be determined easily.
632+
633+
.. function:: getgeneratorstate(generator)
634+
635+
Get current state of a generator-iterator.
636+
637+
Possible states are:
638+
GEN_CREATED: Waiting to start execution.
639+
GEN_RUNNING: Currently being executed by the interpreter.
640+
GEN_SUSPENDED: Currently suspended at a yield expression.
641+
GEN_CLOSED: Execution has completed.
642+
643+
644+

Doc/whatsnew/3.2.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,14 @@ New, Improved, and Deprecated Modules
554554
(Contributed by R. David Murray, :issue:`10321`.)
555555

556556

557+
* The :mod:`inspect` module has a new function :func:`getgenatorstate`
558+
to easily identify the current state of a generator as one of
559+
``GEN_CREATED``, ``GEN_RUNNING``, ``GEN_SUSPENDED`` or ``GEN_CLOSED``.
560+
561+
(Contributed by Rodolpho Eckhardt and Nick Coghlan, :issue:`10220`.)
562+
563+
.. XXX: Mention inspect.getattr_static (Michael Foord)
564+
557565
Multi-threading
558566
===============
559567

Lib/inspect.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1128,3 +1128,23 @@ def getattr_static(obj, attr, default=_sentinel):
11281128
if default is not _sentinel:
11291129
return default
11301130
raise AttributeError(attr)
1131+
1132+
1133+
GEN_CREATED, GEN_RUNNING, GEN_SUSPENDED, GEN_CLOSED = range(4)
1134+
1135+
def getgeneratorstate(generator):
1136+
"""Get current state of a generator-iterator.
1137+
1138+
Possible states are:
1139+
GEN_CREATED: Waiting to start execution.
1140+
GEN_RUNNING: Currently being executed by the interpreter.
1141+
GEN_SUSPENDED: Currently suspended at a yield expression.
1142+
GEN_CLOSED: Execution has completed.
1143+
"""
1144+
if generator.gi_running:
1145+
return GEN_RUNNING
1146+
if generator.gi_frame is None:
1147+
return GEN_CLOSED
1148+
if generator.gi_frame.f_lasti == -1:
1149+
return GEN_CREATED
1150+
return GEN_SUSPENDED

Lib/test/test_inspect.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,12 +887,57 @@ class Something(Base, metaclass=Meta):
887887
self.assertEqual(inspect.getattr_static(Something, 'foo'), 3)
888888

889889

890+
class TestGetGeneratorState(unittest.TestCase):
891+
892+
def setUp(self):
893+
def number_generator():
894+
for number in range(5):
895+
yield number
896+
self.generator = number_generator()
897+
898+
def _generatorstate(self):
899+
return inspect.getgeneratorstate(self.generator)
900+
901+
def test_created(self):
902+
self.assertEqual(self._generatorstate(), inspect.GEN_CREATED)
903+
904+
def test_suspended(self):
905+
next(self.generator)
906+
self.assertEqual(self._generatorstate(), inspect.GEN_SUSPENDED)
907+
908+
def test_closed_after_exhaustion(self):
909+
for i in self.generator:
910+
pass
911+
self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
912+
913+
def test_closed_after_immediate_exception(self):
914+
with self.assertRaises(RuntimeError):
915+
self.generator.throw(RuntimeError)
916+
self.assertEqual(self._generatorstate(), inspect.GEN_CLOSED)
917+
918+
def test_running(self):
919+
# As mentioned on issue #10220, checking for the RUNNING state only
920+
# makes sense inside the generator itself.
921+
# The following generator checks for this by using the closure's
922+
# reference to self and the generator state checking helper method
923+
def running_check_generator():
924+
for number in range(5):
925+
self.assertEqual(self._generatorstate(), inspect.GEN_RUNNING)
926+
yield number
927+
self.assertEqual(self._generatorstate(), inspect.GEN_RUNNING)
928+
self.generator = running_check_generator()
929+
# Running up to the first yield
930+
next(self.generator)
931+
# Running after the first yield
932+
next(self.generator)
933+
934+
890935
def test_main():
891936
run_unittest(
892937
TestDecorators, TestRetrievingSourceCode, TestOneliners, TestBuggyCases,
893938
TestInterpreterStack, TestClassesAndFunctions, TestPredicates,
894939
TestGetcallargsFunctions, TestGetcallargsMethods,
895-
TestGetcallargsUnboundMethods, TestGetattrStatic
940+
TestGetcallargsUnboundMethods, TestGetattrStatic, TestGetGeneratorState
896941
)
897942

898943
if __name__ == "__main__":

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ Core and Builtins
3232
Library
3333
-------
3434

35+
- Issue #10220: Added inspect.getgeneratorstate. Initial patch by
36+
Rodolpho Eckhardt.
37+
3538
- Issue #10453: compileall now uses argparse instead of getopt, and thus
3639
provides clean output when called with '-h'.
3740

0 commit comments

Comments
 (0)