[pypy-commit] pypy py3.5-refactor-sys_exc_info: (arigo, cfbolz): continue refactoring
cfbolz
pypy.commits at gmail.com
Wed Nov 16 11:37:21 EST 2016
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: py3.5-refactor-sys_exc_info
Changeset: r88408:b4bb834f45e9
Date: 2016-11-16 16:47 +0000
http://bitbucket.org/pypy/pypy/changeset/b4bb834f45e9/
Log: (arigo, cfbolz): continue refactoring
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -622,8 +622,8 @@
ops.PRINT_EXPR: -1,
- ops.WITH_CLEANUP_START: 1,
- ops.WITH_CLEANUP_FINISH: -2,
+ ops.WITH_CLEANUP_START: 0,
+ ops.WITH_CLEANUP_FINISH: -1,
ops.LOAD_BUILD_CLASS: 1,
ops.POP_BLOCK: 0,
ops.POP_EXCEPT: -1,
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -226,7 +226,6 @@
self._trace(frame, 'exception', None, operationerr)
#operationerr.print_detailed_traceback(self.space)
- @jit.dont_look_inside
def sys_exc_info(self):
"""Implements sys.exc_info().
Return an OperationError instance or None.
diff --git a/pypy/interpreter/generator.py b/pypy/interpreter/generator.py
--- a/pypy/interpreter/generator.py
+++ b/pypy/interpreter/generator.py
@@ -22,6 +22,7 @@
self._qualname = qualname # may be null, use get_qualname()
if self.pycode.co_flags & CO_YIELD_INSIDE_TRY:
self.register_finalizer(self.space)
+ self.saved_operr = None
def get_name(self):
# 'name' is a byte string that is valid utf-8
@@ -46,6 +47,7 @@
unicode(addrstring)))
def descr__reduce__(self, space):
+ # DEAD CODE, see frame.__reduce__
from pypy.interpreter.mixedmodule import MixedModule
w_mod = space.getbuiltinmodule('_pickle_support')
mod = space.interp_w(MixedModule, w_mod)
@@ -61,6 +63,7 @@
space.newtuple(tup)])
def descr__setstate__(self, space, w_args):
+ # DEAD CODE, see frame.__reduce__
from rpython.rlib.objectmodel import instantiate
args_w = space.unpackiterable(w_args)
w_framestate, w_running = args_w
@@ -136,6 +139,10 @@
space = self.space
if self.running:
raise oefmt(space.w_ValueError, "%s already executing", self.KIND)
+ ec = space.getexecutioncontext()
+ current_exc_info = ec.sys_exc_info()
+ if self.saved_operr is not None:
+ ec.set_sys_exc_info(self.saved_operr)
self.running = True
try:
w_result = frame.execute_frame(self, w_arg_or_err)
@@ -150,6 +157,8 @@
finally:
frame.f_backref = jit.vref_None
self.running = False
+ self.saved_operr = ec.sys_exc_info()
+ ec.set_sys_exc_info(current_exc_info)
return w_result
def resume_execute_frame(self, frame, w_arg_or_err):
@@ -162,7 +171,7 @@
try:
self.next_yield_from(frame, w_yf, w_arg_or_err)
except OperationError as operr:
- operr.record_context(space, frame)
+ operr.record_context(space, space.getexecutioncontext())
return frame.handle_generator_error(operr)
# Normal case: the call above raises Yield.
# We reach this point if the iterable is exhausted.
@@ -226,7 +235,7 @@
space.wrap("%s raised StopIteration" %
self.KIND))
e2.chain_exceptions(space, e)
- e2.record_context(space, self.frame)
+ e2.record_context(space, space.getexecutioncontext())
raise e2
else:
space.warn(space.wrap(u"generator '%s' raised StopIteration"
diff --git a/pypy/interpreter/pyframe.py b/pypy/interpreter/pyframe.py
--- a/pypy/interpreter/pyframe.py
+++ b/pypy/interpreter/pyframe.py
@@ -457,6 +457,9 @@
@jit.dont_look_inside
def descr__reduce__(self, space):
+ # DEAD CODE AHEAD: frame.__reduce__() has been removed.
+ # Either re-add at some point, or kill this code.
+ dead_code
from pypy.interpreter.mixedmodule import MixedModule
w_mod = space.getbuiltinmodule('_pickle_support')
mod = space.interp_w(MixedModule, w_mod)
@@ -467,6 +470,8 @@
@jit.dont_look_inside
def _reduce_state(self, space):
+ # DEAD CODE AHEAD: frame.__reduce__() has been removed.
+ dead_code
from pypy.module._pickle_support import maker # helper fns
w = space.wrap
nt = space.newtuple
@@ -515,6 +520,9 @@
@jit.dont_look_inside
def descr__setstate__(self, space, w_args):
+ # DEAD CODE AHEAD: frame.__reduce__() has been removed.
+ # Either re-add at some point, or kill this code.
+ dead_code
from pypy.module._pickle_support import maker # helper fns
from pypy.interpreter.pycode import PyCode
from pypy.interpreter.module import Module
@@ -882,23 +890,6 @@
return space.wrap(self.builtin is not space.builtin)
return space.w_False
- @jit.unroll_safe
- @specialize.arg(2)
- def _exc_info_unroll(self, space, for_hidden=False):
- """Return the most recent OperationError being handled in the
- call stack
- """
- frame = self
- while frame:
- last = frame.last_exception
- if last is not None:
- if last is get_cleared_operation_error(self.space):
- break
- if for_hidden or not frame.hide():
- return last
- frame = frame.f_backref()
- return None
-
def get_generator(self):
if self.space.config.translation.rweakref:
return self.f_generator_wref()
@@ -906,10 +897,9 @@
return self.f_generator_nowref
def descr_clear(self, space):
+ """F.clear(): clear most references held by the frame"""
# Clears a random subset of the attributes (e.g. 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).
+ # locals, but not f_locals).
if not self.frame_finished_execution:
if not self._is_generator_or_coroutine():
raise oefmt(space.w_RuntimeError,
@@ -923,7 +913,6 @@
# awaited" in this case too. Does it make any sense?
gen.descr_close()
- self.last_exception = None
debug = self.getdebug()
if debug is not None:
debug.w_f_trace = None
@@ -937,6 +926,7 @@
w_newvalue = None
self.locals_cells_stack_w[i] = w_newvalue
self.valuestackdepth = 0
+ self.lastblock = None # the FrameBlock chained list
def _convert_unexpected_exception(self, e):
from pypy.interpreter import error
diff --git a/pypy/interpreter/pyopcode.py b/pypy/interpreter/pyopcode.py
--- a/pypy/interpreter/pyopcode.py
+++ b/pypy/interpreter/pyopcode.py
@@ -63,10 +63,7 @@
try:
while True:
next_instr = self.handle_bytecode(co_code, next_instr, ec)
- except Return:
- self.last_exception = None
- return self.popvalue()
- except Yield:
+ except ExitFrame:
return self.popvalue()
def handle_bytecode(self, co_code, next_instr, ec):
@@ -746,11 +743,13 @@
self.setdictscope(w_locals)
def POP_EXCEPT(self, oparg, next_instr):
- pass # no-op for now: done by the END_FINALLY that follows anyway
+ block = self.pop_block()
+ assert isinstance(block, SysExcInfoRestorer)
+ block.cleanupstack(self) # restores ec.sys_exc_operror
def POP_BLOCK(self, oparg, next_instr):
block = self.pop_block()
- block.cleanup(self) # the block knows how to clean up the value stack
+ block.pop_block(self) # the block knows how to clean up the value stack
def save_and_change_sys_exc_info(self, operationerr):
ec = self.space.getexecutioncontext()
@@ -774,7 +773,7 @@
block = self.pop_block()
assert isinstance(block, SysExcInfoRestorer)
- block.cleanupstack() # restores ec.sys_exc_operror
+ block.cleanupstack(self) # restores ec.sys_exc_operror
w_top = self.popvalue()
if self.space.is_w(w_top, self.space.w_None):
@@ -1161,41 +1160,28 @@
if isinstance(w_unroller, SApplicationException):
# app-level exception
operr = w_unroller.operr
- # this looks again like the kind of code we have. except that it's a call, not a block
- old_last_exception = self.last_exception
- self.last_exception = operr
w_traceback = self.space.wrap(operr.get_traceback())
- try:
- w_res = self.call_contextmanager_exit_function(
- w_exitfunc,
- operr.w_type,
- operr.get_w_value(self.space),
- w_traceback)
- except:
- self.last_exception = old_last_exception
- raise
- # push a marker that also contains the old_last_exception,
- # which must be restored as 'self.last_exception' but only
- # in WITH_CLEANUP_FINISH
- self.pushvalue(SApplicationException(old_last_exception))
+ w_res = self.call_contextmanager_exit_function(
+ w_exitfunc,
+ operr.w_type,
+ operr.get_w_value(self.space),
+ w_traceback)
else:
w_res = self.call_contextmanager_exit_function(
w_exitfunc,
self.space.w_None,
self.space.w_None,
self.space.w_None)
- self.pushvalue(self.space.w_None)
self.pushvalue(w_res)
+ # in the stack now: [w_res, w_unroller-or-w_None..]
def WITH_CLEANUP_FINISH(self, oparg, next_instr):
w_suppress = self.popvalue()
- w_marker = self.popvalue()
- if isinstance(w_marker, SApplicationException):
- self.last_exception = w_marker.operr # may be None
- if self.space.is_true(w_suppress):
- # __exit__() returned True -> Swallow the exception.
- self.settopvalue(self.space.w_None)
+ if self.space.is_true(w_suppress):
+ # __exit__() returned True -> Swallow the exception.
+ self.settopvalue(self.space.w_None)
# this is always followed by END_FINALLY
+ # in the stack now: [w_unroller-or-w_None..]
@jit.unroll_safe
def call_function(self, oparg, w_starstar=None, has_vararg=False):
@@ -1476,8 +1462,8 @@
def SETUP_ASYNC_WITH(self, offsettoend, next_instr):
res = self.popvalue()
- block = WithBlock(self.valuestackdepth,
- next_instr + offsettoend, self.lastblock)
+ block = FinallyBlock(self.valuestackdepth,
+ next_instr + offsettoend, self.lastblock)
self.lastblock = block
self.pushvalue(res)
@@ -1559,12 +1545,15 @@
### ____________________________________________________________ ###
+class ExitFrame(Exception):
+ pass
-class Return(Exception):
+
+class Return(ExitFrame):
"""Raised when exiting a frame via a 'return' statement."""
-class Yield(Exception):
+class Yield(ExitFrame):
"""Raised when exiting a frame via a 'yield' statement."""
@@ -1656,7 +1645,7 @@
def cleanupstack(self, frame):
frame.dropvaluesuntil(self.valuestackdepth)
- def cleanup(self, frame):
+ def pop_block(self, frame):
"Clean up a frame when we normally exit the block."
self.cleanupstack(frame)
@@ -1707,6 +1696,9 @@
self.operr = operr
self.previous = previous
+ def pop_block(self, frame):
+ assert False # never called
+
def handle(self, frame, unroller):
assert False # never called
@@ -1763,6 +1755,10 @@
frame.save_and_change_sys_exc_info(operationerr)
return r_uint(self.handlerposition) # jump to the handler
+ def pop_block(self, frame):
+ self.cleanupstack(frame)
+ frame.save_and_change_sys_exc_info(None)
+
block_classes = {'SYS_EXC_INFO_RESTORER': SysExcInfoRestorer,
'SETUP_LOOP': LoopBlock,
diff --git a/pypy/interpreter/test/test_executioncontext.py b/pypy/interpreter/test/test_executioncontext.py
--- a/pypy/interpreter/test/test_executioncontext.py
+++ b/pypy/interpreter/test/test_executioncontext.py
@@ -2,9 +2,8 @@
from pypy.interpreter import executioncontext
from pypy.interpreter.error import OperationError
-class Finished(OperationError):
- def __init__(self):
- OperationError.__init__(self, "exception_class", "exception_value")
+class Finished(Exception):
+ pass
class TestExecutionContext:
diff --git a/pypy/interpreter/test/test_generator.py b/pypy/interpreter/test/test_generator.py
--- a/pypy/interpreter/test/test_generator.py
+++ b/pypy/interpreter/test/test_generator.py
@@ -457,6 +457,46 @@
assert closed == [True]
"""
+ def test_exc_info_in_generator(self):
+ import sys
+ def g():
+ try:
+ raise ValueError
+ except ValueError:
+ yield sys.exc_info()[0]
+ yield sys.exc_info()[0]
+ try:
+ raise IndexError
+ except IndexError:
+ gen = g()
+ assert sys.exc_info()[0] is IndexError
+ assert next(gen) is ValueError
+ assert sys.exc_info()[0] is IndexError
+ assert next(gen) is ValueError
+ assert sys.exc_info()[0] is IndexError
+ raises(StopIteration, next, gen)
+ assert sys.exc_info()[0] is IndexError
+
+ def test_exc_info_in_generator_2(self):
+ import sys
+ def g():
+ yield sys.exc_info()[0]
+ try:
+ raise LookupError
+ except LookupError:
+ yield sys.exc_info()[0]
+ yield sys.exc_info()[0]
+ try:
+ raise IndexError
+ except IndexError:
+ gen = g() # the IndexError is not captured at all
+ try:
+ raise ValueError
+ except ValueError:
+ assert next(gen) is ValueError
+ assert next(gen) is LookupError
+ assert next(gen) is ValueError
+
def test_should_not_inline(space):
from pypy.interpreter.generator import should_not_inline
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
@@ -148,30 +148,6 @@
assert f1bis is f1
assert f0.f_back is f1
- def test_f_exc_xxx(self):
- import sys
-
- class OuterException(Exception):
- pass
- class InnerException(Exception):
- pass
-
- def g(exc_info):
- f = sys._getframe()
- assert f.f_exc_type is None
- assert f.f_exc_value is None
- assert f.f_exc_traceback is None
- try:
- raise InnerException
- except:
- assert f.f_exc_type is exc_info[0]
- assert f.f_exc_value is exc_info[1]
- assert f.f_exc_traceback is exc_info[2]
- try:
- raise OuterException
- except:
- g(sys.exc_info())
-
def test_virtualref_through_traceback(self):
import sys
def g():
diff --git a/pypy/interpreter/test/test_zzpickle_and_slow.py b/pypy/interpreter/test/test_zzpickle_and_slow.py
--- a/pypy/interpreter/test/test_zzpickle_and_slow.py
+++ b/pypy/interpreter/test/test_zzpickle_and_slow.py
@@ -351,6 +351,7 @@
assert list(result) == [2,3,4]
def test_pickle_generator(self):
+ skip("not supported any more for now")
self.skip_on_cpython()
import types
mod = types.ModuleType('mod')
@@ -375,6 +376,7 @@
del sys.modules['mod']
def test_pickle_generator_blk(self):
+ skip("not supported any more for now")
self.skip_on_cpython()
# same as above but with the generator inside a block
import types
@@ -448,6 +450,7 @@
def test_pickle_generator_crash(self):
+ skip("not supported any more for now")
self.skip_on_cpython()
import pickle
@@ -464,7 +467,7 @@
assert 'finished' in repr(y)
assert y.gi_code is None
-class AppTestGeneratorCloning:
+class XAppTestGeneratorCloning:
def setup_class(cls):
try:
@@ -528,7 +531,7 @@
raises(StopIteration, next, g3)
raises(StopIteration, next, g4)
-class AppTestFramePickling(object):
+class XAppTestFramePickling(object):
pytestmark = py.test.mark.skipif("config.option.runappdirect")
spaceconfig = {
"usemodules": ["struct"]
diff --git a/pypy/interpreter/typedef.py b/pypy/interpreter/typedef.py
--- a/pypy/interpreter/typedef.py
+++ b/pypy/interpreter/typedef.py
@@ -607,8 +607,8 @@
PyCode.typedef.acceptable_as_base_class = False
PyFrame.typedef = TypeDef('frame',
- __reduce__ = interp2app(PyFrame.descr__reduce__),
- __setstate__ = interp2app(PyFrame.descr__setstate__),
+ #__reduce__ = interp2app(PyFrame.descr__reduce__), --- logic not updated
+ #__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),
@@ -784,8 +784,8 @@
GeneratorIterator.typedef = TypeDef("generator",
__repr__ = interp2app(GeneratorIterator.descr__repr__),
- __reduce__ = interp2app(GeneratorIterator.descr__reduce__),
- __setstate__ = interp2app(GeneratorIterator.descr__setstate__),
+ #__reduce__ = interp2app(GeneratorIterator.descr__reduce__),
+ #__setstate__ = interp2app(GeneratorIterator.descr__setstate__),
__next__ = interp2app(GeneratorIterator.descr_next,
descrmismatch='__next__'),
send = interp2app(GeneratorIterator.descr_send,
diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py
--- a/pypy/module/pypyjit/interp_jit.py
+++ b/pypy/module/pypyjit/interp_jit.py
@@ -26,7 +26,6 @@
'valuestackdepth',
'locals_cells_stack_w[*]',
'debugdata',
- 'last_exception',
'lastblock',
'w_globals',
]
@@ -98,7 +97,6 @@
jit.hint(self, force_virtualizable=True)
return w_result
except Return:
- self.last_exception = None
return self.popvalue()
def jump_absolute(self, jumpto, ec):
diff --git a/pypy/module/sys/vm.py b/pypy/module/sys/vm.py
--- a/pypy/module/sys/vm.py
+++ b/pypy/module/sys/vm.py
@@ -107,8 +107,7 @@
return space.newtuple([operror.w_type, operror.get_w_value(space),
space.wrap(operror.get_traceback())])
-def exc_info_without_tb(space, frame):
- operror = frame.last_exception
+def exc_info_without_tb(space, operror):
return space.newtuple([operror.w_type, operror.get_w_value(space),
space.w_None])
@@ -152,10 +151,11 @@
if space.int_w(w_constant) <= 2:
need_all_three_args = False
#
- if need_all_three_args or frame.last_exception is None or frame.hide():
+ operror = space.getexecutioncontext().sys_exc_info()
+ if need_all_three_args or operror is None or frame.hide():
return exc_info_with_tb(space)
else:
- return exc_info_without_tb(space, frame)
+ return exc_info_without_tb(space, operror)
def exc_clear(space):
"""Clear global information on the current exception. Subsequent calls
diff --git a/pypy/tool/pytest/appsupport.py b/pypy/tool/pytest/appsupport.py
--- a/pypy/tool/pytest/appsupport.py
+++ b/pypy/tool/pytest/appsupport.py
@@ -210,26 +210,20 @@
space.newtuple([w_BuiltinAssertionError]),
w_dict)
-def _exc_info(space, err):
- """Hack the fact that exc_info() isn't set until a app except
- block catches it."""
- err.normalize_exception(space)
- frame = space.getexecutioncontext().gettopframe()
- old = frame.last_exception
- frame.last_exception = err
- if not hasattr(space, '_w_ExceptionInfo'):
- space._w_ExceptionInfo = space.appexec([], """():
- class _ExceptionInfo(object):
- def __init__(self):
- import sys
- self.type, self.value, self.traceback = sys.exc_info()
-
- return _ExceptionInfo
-""")
- try:
- return space.call_function(space._w_ExceptionInfo)
- finally:
- frame.last_exception = old
+def _exc_info(space, operror):
+ """sys.exc_info() isn't set until a app except block catches it,
+ but we can directly copy the two lines of code from module/sys/vm.py."""
+ operror.normalize_exception(space)
+ return space.appexec([operror.w_type, operror.get_w_value(space),
+ space.wrap(operror.get_traceback())], """(t, v, tb):
+ class _ExceptionInfo:
+ pass
+ e = _ExceptionInfo()
+ e.type = t
+ e.value = v
+ e.traceback = tb
+ return e
+ """)
def pypyraises(space, w_ExpectedException, w_expr, __args__):
"""A built-in function providing the equivalent of py.test.raises()."""
More information about the pypy-commit
mailing list