[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