[pypy-svn] r10369 - in pypy/branch/pypy-normalize-exception: . documentation interpreter objspace/flow tool
arigo at codespeak.net
arigo at codespeak.net
Wed Apr 6 18:54:52 CEST 2005
Author: arigo
Date: Wed Apr 6 18:54:52 2005
New Revision: 10369
Added:
pypy/branch/pypy-normalize-exception/
- copied from r10364, pypy/dist/pypy/
Modified:
pypy/branch/pypy-normalize-exception/TODO
pypy/branch/pypy-normalize-exception/documentation/controlflow.txt
pypy/branch/pypy-normalize-exception/interpreter/baseobjspace.py
pypy/branch/pypy-normalize-exception/interpreter/error.py
pypy/branch/pypy-normalize-exception/interpreter/gateway.py
pypy/branch/pypy-normalize-exception/interpreter/pyframe.py
pypy/branch/pypy-normalize-exception/interpreter/pyopcode.py
pypy/branch/pypy-normalize-exception/objspace/flow/objspace.py
pypy/branch/pypy-normalize-exception/tool/pytestsupport.py
Log:
A branch to share some ideas being developed on IRC.
Modified: pypy/branch/pypy-normalize-exception/TODO
==============================================================================
--- pypy/dist/pypy/TODO (original)
+++ pypy/branch/pypy-normalize-exception/TODO Wed Apr 6 18:54:52 2005
@@ -33,6 +33,8 @@
* review whatever you like
+* sys.last_type/last_value are not normalized -- bug
+
StdObjSpace
===========
Modified: pypy/branch/pypy-normalize-exception/documentation/controlflow.txt
==============================================================================
--- pypy/dist/pypy/documentation/controlflow.txt (original)
+++ pypy/branch/pypy-normalize-exception/documentation/controlflow.txt Wed Apr 6 18:54:52 2005
@@ -90,6 +90,9 @@
A Constant can occasionally store a mutable Python object. It represents a static, pre-initialized, read-only version of that object. The flow graph should not attempt to actually mutate such Constants.
+XXX talk about implicit exceptions
+
+
How the FlowObjSpace works
--------------------------
Modified: pypy/branch/pypy-normalize-exception/interpreter/baseobjspace.py
==============================================================================
--- pypy/dist/pypy/interpreter/baseobjspace.py (original)
+++ pypy/branch/pypy-normalize-exception/interpreter/baseobjspace.py Wed Apr 6 18:54:52 2005
@@ -214,10 +214,6 @@
check_list.extend(exclst)
return False
- def normalize_exception(self, w_type, w_value, w_tb):
- from pypy.interpreter import pyframe
- return pyframe.normalize_exception(self, w_type,w_value, w_tb)
-
def call(self, w_callable, w_args, w_kwds=None):
args = Arguments.frompacked(self, w_args, w_kwds)
return self.call_args(w_callable, args)
@@ -266,6 +262,16 @@
except OperationError:
return self.w_False
+ def abstract_isclass(self, w_obj):
+ if self.is_true(self.isinstance(w_obj, self.w_type)):
+ return self.w_True
+ try:
+ self.getattr(w_obj, self.wrap('__bases__'))
+ except OperationError:
+ return self.w_False
+ else:
+ return self.w_True
+
def eval(self, expression, w_globals, w_locals):
"NOT_RPYTHON: For internal debugging."
Modified: pypy/branch/pypy-normalize-exception/interpreter/error.py
==============================================================================
--- pypy/dist/pypy/interpreter/error.py (original)
+++ pypy/branch/pypy-normalize-exception/interpreter/error.py Wed Apr 6 18:54:52 2005
@@ -122,6 +122,74 @@
import debug
debug.fire(self)
+ def normalize_exception(self, space):
+ """Normalize the OperationError. In other words, fix w_type and/or
+ w_value to make sure that the __class__ of w_value is exactly w_type.
+ """
+ w_type = self.w_type
+ w_value = self.w_value
+ if space.full_exceptions:
+ while space.is_true(space.isinstance(w_type, space.w_tuple)):
+ w_type = space.getitem(w_type, space.wrap(0))
+
+ if space.is_true(space.abstract_isclass(w_type)):
+ if space.is_w(w_value, space.w_None):
+ # raise Type: we assume we have to instantiate Type
+ w_value = space.call_function(w_type)
+ w_type = space.getattr(w_value, space.wrap('__class__'))
+ else:
+ w_valuetype = space.getattr(w_value, space.wrap('__class__'))
+ if space.is_true(space.abstract_issubclass(w_valuetype,
+ w_type)):
+ # raise Type, Instance: let etype be the exact type of value
+ w_type = w_valuetype
+ else:
+ if space.full_exceptions and space.is_true(
+ space.isinstance(w_value, space.w_tuple)):
+ # raise Type, tuple: assume the tuple contains the
+ # constructor args
+ w_value = space.call(w_type, w_value)
+ else:
+ # raise Type, X: assume X is the constructor argument
+ w_value = space.call_function(w_type, w_value)
+ w_type = space.getattr(w_value, space.wrap('__class__'))
+
+ elif space.full_exceptions and space.is_w(space.type(w_type),
+ space.w_str):
+ # XXX warn -- deprecated
+ if (space.is_w(w_value, space.w_None) or
+ space.is_w(space.type(w_value), space.w_str)):
+ pass # ok
+ else:
+ raise OperationError(space.w_TypeError,
+ space.wrap("string exceptions can only "
+ "have a string value"))
+ else:
+
+ # raise X: we assume that X is an already-built instance
+ if not space.is_w(w_value, space.w_None):
+ raise OperationError(space.w_TypeError,
+ space.wrap("instance exception may not "
+ "have a separate value"))
+ w_value = w_type
+ w_type = space.getattr(w_value, space.wrap('__class__'))
+ if space.full_exceptions:
+ # for the sake of language consistency we should not allow
+ # things like 'raise 1', but it is probably fine (i.e.
+ # not ambiguous) to allow them in the explicit form 'raise int, 1'
+ try:
+ w_module = space.getattr(w_type, space.wrap('__module__'))
+ except OperationError:
+ pass
+ else:
+ if space.eq_w(w_module, space.wrap('__builtin__')):
+ raise OperationError(space.w_TypeError,
+ space.wrap("raising built-in objects can "
+ "be ambiguous, "
+ "use 'raise type, value' instead"))
+ self.w_type = w_type
+ self.w_value = w_value
+
# Utilities
from pypy.tool.ansi_print import ansi_print
Modified: pypy/branch/pypy-normalize-exception/interpreter/gateway.py
==============================================================================
--- pypy/dist/pypy/interpreter/gateway.py (original)
+++ pypy/branch/pypy-normalize-exception/interpreter/gateway.py Wed Apr 6 18:54:52 2005
@@ -567,7 +567,7 @@
return klass(source, filename, modname, do_imports)
# uncomment this to check against applevel without translation
-##ApplevelInterpClass = ApplevelClass
+ApplevelInterpClass = ApplevelClass
def appdef(source, applevel=ApplevelClass):
""" NOT_RPYTHON: build an app-level helper function, like for example:
Modified: pypy/branch/pypy-normalize-exception/interpreter/pyframe.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyframe.py (original)
+++ pypy/branch/pypy-normalize-exception/interpreter/pyframe.py Wed Apr 6 18:54:52 2005
@@ -395,73 +395,22 @@
# push the exception to the value stack for inspection by the
# exception handler (the code after the except:)
operationerr = unroller.args[0]
- w_type = operationerr.w_type
- w_value = operationerr.w_value
- w_normalized = frame.space.normalize_exception(w_type, w_value,
- frame.space.w_None)
- w_type, w_value, w_tb = frame.space.unpacktuple(w_normalized, 3)
- # save the normalized exception back into the OperationError
- # -- in particular it makes sure that sys.exc_info() etc see
- # normalized exception.
- operationerr.w_type = w_type
- operationerr.w_value = w_value
+ try:
+ operationerr.normalize_exception(frame.space)
+ except OperationError, operationerr:
+ # exception while normalizing the exception!
+ # you get an exception, just not the one you might expect...
+ operationerr.normalize_exception(frame.space)
# the stack setup is slightly different than in CPython:
# instead of the traceback, we store the unroller object,
# wrapped.
frame.valuestack.push(frame.space.wrap(unroller))
- frame.valuestack.push(w_value)
- frame.valuestack.push(w_type)
+ frame.valuestack.push(operationerr.w_value)
+ frame.valuestack.push(operationerr.w_type)
frame.next_instr = self.handlerposition # jump to the handler
return True # stop unrolling
return False
-# make the following flowable: need _classobj
-import types, __builtin__
-__builtin__._classobj = types.ClassType
-
-app = gateway.applevel('''
- def normalize_exception(etype, value, tb):
- """Normalize an (exc_type, exc_value) pair:
- exc_value will be an exception instance and exc_type its class.
- """
- # mistakes here usually show up as infinite recursion, which is fun.
- while isinstance(etype, tuple):
- etype = etype[0]
- ## if isinstance(etype, (type, _classobj)):
- ## isinstance with tuple argument doesn't map to space.isinstance, yet
- if isinstance(etype, type) or isinstance(etype, _classobj):
- if not isinstance(value, etype):
- if value is None:
- # raise Type: we assume we have to instantiate Type
- value = etype()
- elif isinstance(value, tuple):
- # raise Type, Tuple: assume Tuple contains the constructor args
- value = etype(*value)
- else:
- # raise Type, X: assume X is the constructor argument
- value = etype(value)
- # raise Type, Instance: let etype be the exact type of value
- etype = value.__class__
- elif type(etype) is str:
- # XXX warn -- deprecated
- if value is not None and type(value) is not str:
- raise TypeError("string exceptions can only have a string value")
- else:
- # raise X: we assume that X is an already-built instance
- if value is not None:
- raise TypeError("instance exception may not have a separate value")
- value = etype
- etype = value.__class__
- # for the sake of language consistency we should not allow
- # things like 'raise 1', but it is probably fine (i.e.
- # not ambiguous) to allow them in the explicit form 'raise int, 1'
- if not hasattr(value, '__dict__') and not hasattr(value, '__slots__'):
- raise TypeError("raising built-in objects can be ambiguous, "
- "use 'raise type, value' instead")
- return etype, value, tb
-''')
-normalize_exception = app.interphook("normalize_exception")
-
class FinallyBlock(FrameBlock):
"""A try:finally: block. Stores the position of the exception handler."""
Modified: pypy/branch/pypy-normalize-exception/interpreter/pyopcode.py
==============================================================================
--- pypy/dist/pypy/interpreter/pyopcode.py (original)
+++ pypy/branch/pypy-normalize-exception/interpreter/pyopcode.py Wed Apr 6 18:54:52 2005
@@ -312,31 +312,31 @@
# we use the .app.py file to prepare the exception/value/traceback
# but not to actually raise it, because we cannot use the 'raise'
# statement to implement RAISE_VARARGS
+ space = f.space
if nbargs == 0:
- operror = f.space.getexecutioncontext().sys_exc_info()
+ operror = space.getexecutioncontext().sys_exc_info()
if operror is None:
- raise OperationError(f.space.w_TypeError,
- f.space.wrap("raise: no active exception to re-raise"))
+ raise OperationError(space.w_TypeError,
+ space.wrap("raise: no active exception to re-raise"))
# re-raise, no new traceback obj will be attached
raise pyframe.SApplicationException(operror)
- w_value = w_traceback = f.space.w_None
+ w_value = w_traceback = space.w_None
if nbargs >= 3: w_traceback = f.valuestack.pop()
if nbargs >= 2: w_value = f.valuestack.pop()
if 1: w_type = f.valuestack.pop()
- w_resulttuple = f.space.normalize_exception(w_type, w_value,
- w_traceback)
- w_type, w_value, w_traceback = f.space.unpacktuple(w_resulttuple, 3)
- if w_traceback is not f.space.w_None:
- tb = f.space.interpclass_w(w_traceback)
- if not isinstance(tb,pytraceback.PyTraceback):
- raise OperationError(f.space.w_TypeError,
- f.space.wrap("raise: arg 3 must be a traceback or None"))
- operror = OperationError(w_type,w_value,tb)
+ operror = OperationError(w_type, w_value)
+ operror.normalize_exception(space)
+ if space.is_w(w_traceback, space.w_None):
+ # common case
+ raise operror
+ else:
+ tb = space.interpclass_w(w_traceback)
+ if not isinstance(tb, pytraceback.PyTraceback):
+ raise OperationError(space.w_TypeError,
+ space.wrap("raise: arg 3 must be a traceback or None"))
+ operror.application_traceback = tb
# re-raise, no new traceback obj will be attached
raise pyframe.SApplicationException(operror)
- else:
- # common-case
- raise OperationError(w_type, w_value)
def LOAD_LOCALS(f):
f.valuestack.push(f.w_locals)
Modified: pypy/branch/pypy-normalize-exception/objspace/flow/objspace.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/objspace.py (original)
+++ pypy/branch/pypy-normalize-exception/objspace/flow/objspace.py Wed Apr 6 18:54:52 2005
@@ -45,6 +45,8 @@
self.w_None = Constant(None)
self.w_False = Constant(False)
self.w_True = Constant(True)
+ self.w_type = Constant(type)
+ self.w_tuple = Constant(tuple)
for exc in [KeyError, ValueError, IndexError, StopIteration,
AssertionError]:
clsname = exc.__name__
@@ -206,61 +208,14 @@
return ecls
return None
- def normalize_exception(space, w_arg1, w_arg2, w_tb):
- """Special-case for 'raise' statements. Case-by-case analysis:
+ def abstract_issubclass(self, w_obj, w_cls, failhard=False):
+ return self.issubtype(w_obj, w_cls)
- * raise Class
- - with a constant Class, it is easy to recognize.
- But we don't normalize: the associated value is None.
-
- * raise Class(...)
- - when the class is instantiated in-place, we can figure that out
-
- * raise Instance
- - assumes that it's not a class, and raises an exception whose class
- is variable and whose value is Instance.
-
- * raise Class, Arg
- - assumes that Arg is the value you want for the exception, and
- that Class is exactly the exception class. No check or normalization.
- """
+ def abstract_isinstance(self, w_obj, w_cls):
+ return self.isinstance(w_obj, w_cls)
- # w_arg3 (the traceback) is ignored and replaced with None
- # if it is a Variable, because pyopcode.py tries to unwrap it.
- # It means that we ignore the 'tb' argument of 'raise' in most cases.
- if not isinstance(w_tb, Constant):
- w_tb = space.w_None
-
- if w_arg2 != space.w_None:
- # raise Class, Arg: no normalization
- return (w_arg1, w_arg2, w_tb)
-
- etype = space.getconstclass(w_arg1)
- if etype is not None:
- # raise Class
- return (w_arg1, space.w_None, w_tb)
-
- # raise Class(..)? We need a hack to figure out of which class it is.
- # Normally, Instance should have been created by the previous operation
- # which should be a simple_call(<Class>, ...).
- # Fetch the <Class> out of there. (This doesn't work while replaying)
- # XXX this case is likely not triggered anymore, because the instance creation op
- # is walled off in a different block by the surrounding it with exception
- # handling logic that is always put in place for calls.
- # We may want to make this more clever!
- operations = space.executioncontext.recorder.crnt_block.operations
- if operations:
- spaceop = operations[-1]
- if (spaceop.opname == 'simple_call' and
- spaceop.result is w_arg1):
- w_type = spaceop.args[0]
- return (w_type, w_arg1, w_tb)
-
- # raise Instance. Fall-back.
- w_type = space.do_operation('type', w_arg1)
- return (w_type, w_arg1, w_tb)
- # this function returns a real tuple that can be handled
- # by FlowObjSpace.unpacktuple()
+ def abstract_isclass(self, w_obj):
+ return self.isinstance(w_obj, self.w_type)
def build_flow(self, func, constargs={}):
@@ -289,13 +244,13 @@
return ec.graph
def unpacktuple(self, w_tuple, expected_length=None):
- # special case to accept either Constant tuples
- # or real tuples of Variables/Constants
- if isinstance(w_tuple, tuple):
- result = w_tuple
- else:
- unwrapped = self.unwrap(w_tuple)
- result = tuple([Constant(x) for x in unwrapped])
+## # special case to accept either Constant tuples
+## # or real tuples of Variables/Constants
+## if isinstance(w_tuple, tuple):
+## result = w_tuple
+## else:
+ unwrapped = self.unwrap(w_tuple)
+ result = tuple([Constant(x) for x in unwrapped])
if expected_length is not None and len(result) != expected_length:
raise ValueError, "got a tuple of length %d instead of %d" % (
len(result), expected_length)
@@ -469,6 +424,12 @@
x_cell == c
return x.other
+def type_or_something_similar(x):
+ t = type(x)
+ if t is types.ClassType: # guess who's here? exception classes...
+ t = type
+ return t
+
def make_op(name, symbol, arity, specialnames):
if hasattr(FlowObjSpace, name):
return # Shouldn't do it
@@ -482,6 +443,8 @@
# op = apply
if name == 'issubtype':
op = issubclass
+ elif name == 'type':
+ op = type_or_something_similar
elif name == 'is_':
op = lambda x, y: x is y
elif name == 'getattr':
Modified: pypy/branch/pypy-normalize-exception/tool/pytestsupport.py
==============================================================================
--- pypy/dist/pypy/tool/pytestsupport.py (original)
+++ pypy/branch/pypy-normalize-exception/tool/pytestsupport.py Wed Apr 6 18:54:52 2005
@@ -83,10 +83,10 @@
space.call_args(w_parent_init, __args__.prepend(w_self))
framestack = space.getexecutioncontext().framestack
frame = framestack.top(0)
- # Argh! we may see app-level helpers in the frame stack!
- # that's very probably very bad...
- if frame.code.co_name == 'normalize_exception':
- frame = framestack.top(1)
+## # Argh! we may see app-level helpers in the frame stack!
+## # that's very probably very bad...
+## if frame.code.co_name == 'normalize_exception':
+## frame = framestack.top(1)
# if the assertion provided a message, don't do magic
args_w, kwargs_w = __args__.unpack()
More information about the Pypy-commit
mailing list