[pypy-commit] pypy py3.5: frame.clear()
arigo
pypy.commits at gmail.com
Thu Aug 18 10:30:14 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r86274:79296a54da93
Date: 2016-08-18 16:29 +0200
http://bitbucket.org/pypy/pypy/changeset/79296a54da93/
Log: frame.clear()
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -6,7 +6,14 @@
from rpython.rlib import jit
-class GeneratorIterator(W_Root):
+class GeneratorOrCoroutine(W_Root):
+ """XXX: move the common functionality here!"""
+
+ def descr_close(self):
+ raise NotImplementedError
+
+
+class GeneratorIterator(GeneratorOrCoroutine):
"An iterator created by a generator."
_immutable_fields_ = ['pycode']
@@ -333,7 +340,7 @@
get_printable_location = get_printable_coroutine_location_genentry,
name='coroutineentry')
-class Coroutine(W_Root):
+class Coroutine(GeneratorOrCoroutine):
"A coroutine object."
_immutable_fields_ = ['pycode']
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -62,6 +62,7 @@
__metaclass__ = extendabletype
frame_finished_execution = False
+ frame_generator = None # for generators/coroutines
last_instr = -1
last_exception = None
f_backref = jit.vref_None
@@ -240,12 +241,16 @@
def run(self):
"""Start this frame's execution."""
- if self.getcode().co_flags & pycode.CO_COROUTINE:
- from pypy.interpreter.generator import Coroutine
- return self.space.wrap(Coroutine(self))
- elif self.getcode().co_flags & pycode.CO_GENERATOR:
- from pypy.interpreter.generator import GeneratorIterator
- return self.space.wrap(GeneratorIterator(self))
+ if self.getcode().co_flags & (pycode.CO_COROUTINE |
+ pycode.CO_GENERATOR):
+ if self.getcode().co_flags & pycode.CO_COROUTINE:
+ from pypy.interpreter.generator import Coroutine
+ gen = Coroutine(self)
+ else:
+ from pypy.interpreter.generator import GeneratorIterator
+ gen = GeneratorIterator(self)
+ self.frame_generator = gen
+ return self.space.wrap(gen)
else:
return self.execute_frame()
@@ -886,6 +891,29 @@
frame = frame.f_backref()
return None
+ def descr_clear(self, space):
+ # Clears a random subset of the attributes (e.g. some the fast
+ # locals, but not f_locals). Also clears last_exception, which
+ # is not quite like CPython when it clears f_exc_* (however
+ # there might not be an observable difference).
+ if not self.frame_finished_execution:
+ if self.frame_generator is None or self.frame_generator.running:
+ raise oefmt(space.w_RuntimeError,
+ "cannot clear an executing frame")
+ # xxx CPython raises the RuntimeWarning "coroutine was never
+ # awaited" in this case too. Does it make any sense?
+ self.frame_generator.descr_close()
+
+ self.last_exception = None
+ debug = self.getdebug()
+ if debug is not None:
+ debug.w_f_trace = None
+
+ # clear the locals, including the cell/free vars, and the stack
+ for i in range(len(self.locals_cells_stack_w)):
+ self.locals_cells_stack_w[i] = None
+ self.valuestackdepth = 0
+
# ____________________________________________________________
def get_block_class(opname):
diff --git a/pypy/interpreter/test/test_pyframe.py b/pypy/interpreter/test/test_pyframe.py
--- a/pypy/interpreter/test/test_pyframe.py
+++ b/pypy/interpreter/test/test_pyframe.py
@@ -545,3 +545,41 @@
it = yield_raise()
assert next(it) is KeyError
assert next(it) is KeyError
+
+ def test_frame_clear(self):
+ import sys, gc, weakref
+ #
+ raises(RuntimeError, sys._getframe().clear)
+ def g():
+ yield 5
+ raises(RuntimeError, sys._getframe().clear)
+ yield 6
+ assert list(g()) == [5, 6]
+ #
+ class A:
+ pass
+ a1 = A(); a1ref = weakref.ref(a1)
+ a2 = A(); a2ref = weakref.ref(a2)
+ seen = []
+ def f():
+ local_a1 = a1
+ for loc in [5, 6, a2]:
+ try:
+ yield sys._getframe()
+ finally:
+ seen.append(42)
+ seen.append(43)
+ gen = f()
+ frame = next(gen)
+ a1 = a2 = None
+ gc.collect(); gc.collect()
+ assert a1ref() is not None
+ assert a2ref() is not None
+ assert seen == []
+ frame.clear()
+ assert seen == [42]
+ gc.collect(); gc.collect()
+ assert a1ref() is None, "locals not cleared"
+ assert a2ref() is None, "stack not cleared"
+ #
+ raises(StopIteration, next, gen)
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -605,6 +605,7 @@
PyFrame.typedef = TypeDef('frame',
__reduce__ = interp2app(PyFrame.descr__reduce__),
__setstate__ = interp2app(PyFrame.descr__setstate__),
+ clear = interp2app(PyFrame.descr_clear),
f_builtins = GetSetProperty(PyFrame.fget_f_builtins),
f_lineno = GetSetProperty(PyFrame.fget_f_lineno, PyFrame.fset_f_lineno),
f_back = GetSetProperty(PyFrame.fget_f_back),
More information about the pypy-commit
mailing list