[pypy-commit] pypy default: Merged in vext01/pypy/improve-errors-again (pull request #184)

rlamy noreply at buildbot.pypy.org
Wed Aug 28 18:26:24 CEST 2013


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: 
Changeset: r66418:4f1cd13cc351
Date: 2013-08-28 17:25 +0100
http://bitbucket.org/pypy/pypy/changeset/4f1cd13cc351/

Log:	Merged in vext01/pypy/improve-errors-again (pull request #184)

	More improvements and refactorings of error messages.

diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -5,7 +5,7 @@
 from rpython.tool.ansi_print import ansi_log
 from rpython.tool.pairtype import pair
 from rpython.tool.error import (format_blocked_annotation_error,
-                             AnnotatorError, gather_error, ErrorWrapper, source_lines)
+                             gather_error, source_lines)
 from rpython.flowspace.model import (Variable, Constant, FunctionGraph,
                                       c_last_exception, checkgraph)
 from rpython.translator import simplify, transform
@@ -18,7 +18,6 @@
 
 FAIL = object()
 
-
 class RPythonAnnotator(object):
     """Block annotator for RPython.
     See description in doc/translation.txt."""
@@ -137,9 +136,7 @@
         checkgraph(flowgraph)
 
         nbarg = len(flowgraph.getargs())
-        if len(inputcells) != nbarg:
-            raise TypeError("%s expects %d args, got %d" %(
-                            flowgraph, nbarg, len(inputcells)))
+        assert len(inputcells) == nbarg # wrong number of args
 
         # register the entry point
         self.addpendinggraph(flowgraph, inputcells)
@@ -160,7 +157,7 @@
             else:
                 return object
         else:
-            raise TypeError, ("Variable or Constant instance expected, "
+            raise TypeError("Variable or Constant instance expected, "
                               "got %r" % (variable,))
 
     def getuserclassdefinitions(self):
@@ -221,7 +218,7 @@
 
             text = format_blocked_annotation_error(self, self.blocked_blocks)
             #raise SystemExit()
-            raise AnnotatorError(text)
+            raise annmodel.AnnotatorError(text)
         for graph in newgraphs:
             v = graph.getreturnvar()
             if v not in self.bindings:
@@ -244,7 +241,7 @@
             #    return annmodel.s_ImpossibleValue
             return self.bookkeeper.immutableconstant(arg)
         else:
-            raise TypeError, 'Variable or Constant expected, got %r' % (arg,)
+            raise TypeError('Variable or Constant expected, got %r' % (arg,))
 
     def typeannotation(self, t):
         return signature.annotation(t, self.bookkeeper)
@@ -603,10 +600,9 @@
                 raise BlockedInference(self, op, opindex)
         try:
             resultcell = consider_meth(*argcells)
-        except Exception, e:
+        except annmodel.AnnotatorError as e: # note that UnionError is a subclass
             graph = self.bookkeeper.position_key[0]
-            e.args = e.args + (
-                ErrorWrapper(gather_error(self, graph, block, opindex)),)
+            e.source = gather_error(self, graph, block, opindex)
             raise
         if resultcell is None:
             resultcell = self.noreturnvalue(op)
diff --git a/rpython/annotator/argument.py b/rpython/annotator/argument.py
--- a/rpython/annotator/argument.py
+++ b/rpython/annotator/argument.py
@@ -115,9 +115,7 @@
         elif num_args > co_argcount:
             raise ArgErrCount(num_args, num_kwds, signature, defaults_w, 0)
 
-        # if a **kwargs argument is needed, explode
-        if signature.has_kwarg():
-            raise TypeError("Keyword arguments as **kwargs is not supported by RPython")
+        assert not signature.has_kwarg() # XXX should not happen?
 
         # handle keyword arguments
         num_remainingkwds = 0
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -20,7 +20,7 @@
 from rpython.annotator.bookkeeper import getbookkeeper
 from rpython.flowspace.model import Variable, Constant
 from rpython.rlib import rarithmetic
-from rpython.tool.error import AnnotatorError
+from rpython.annotator.model import AnnotatorError
 
 # convenience only!
 def immutablevalue(x):
@@ -457,7 +457,7 @@
 class __extend__(pairtype(SomeString, SomeUnicodeString),
                  pairtype(SomeUnicodeString, SomeString)):
     def mod((str, unistring)):
-        raise NotImplementedError(
+        raise AnnotatorError(
             "string formatting mixing strings and unicode not supported")
 
 
@@ -471,7 +471,7 @@
             if (is_unicode and isinstance(s_item, (SomeChar, SomeString)) or
                 is_string and isinstance(s_item, (SomeUnicodeCodePoint,
                                                   SomeUnicodeString))):
-                raise NotImplementedError(
+                raise AnnotatorError(
                     "string formatting mixing strings and unicode not supported")
         getbookkeeper().count('strformat', s_string, s_tuple)
         no_nul = s_string.no_nul
diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py
--- a/rpython/annotator/builtin.py
+++ b/rpython/annotator/builtin.py
@@ -337,7 +337,7 @@
     return SomeAddress()
 
 def unicodedata_decimal(s_uchr):
-    raise TypeError, "unicodedate.decimal() calls should not happen at interp-level"
+    raise TypeError("unicodedate.decimal() calls should not happen at interp-level")
 
 def test(*args):
     return s_Bool
diff --git a/rpython/annotator/classdef.py b/rpython/annotator/classdef.py
--- a/rpython/annotator/classdef.py
+++ b/rpython/annotator/classdef.py
@@ -2,7 +2,7 @@
 Type inference for user-defined classes.
 """
 from rpython.annotator.model import SomePBC, s_ImpossibleValue, unionof
-from rpython.annotator.model import SomeInteger, SomeTuple, SomeString
+from rpython.annotator.model import SomeInteger, SomeTuple, SomeString, AnnotatorError
 from rpython.annotator import description
 
 
@@ -429,7 +429,7 @@
                         result.extend(slots)
         return result
 
-class NoSuchAttrError(Exception):
+class NoSuchAttrError(AnnotatorError):
     """Raised when an attribute is found on a class where __slots__
      or _attrs_ forbits it."""
 
diff --git a/rpython/annotator/description.py b/rpython/annotator/description.py
--- a/rpython/annotator/description.py
+++ b/rpython/annotator/description.py
@@ -6,6 +6,7 @@
 from rpython.annotator.argument import rawshape, ArgErr
 from rpython.tool.sourcetools import valid_identifier, func_with_new_name
 from rpython.tool.pairtype import extendabletype
+from rpython.annotator.model import AnnotatorError
 
 class CallFamily(object):
     """A family of Desc objects that could be called from common call sites.
@@ -261,7 +262,7 @@
         try:
             inputcells = args.match_signature(signature, defs_s)
         except ArgErr, e:
-            raise TypeError("signature mismatch: %s() %s" %
+            raise AnnotatorError("signature mismatch: %s() %s" %
                             (self.name, e.getmsg()))
         return inputcells
 
@@ -678,7 +679,7 @@
                 value = value.__get__(42)
                 classdef = None   # don't bind
             elif isinstance(value, classmethod):
-                raise AssertionError("classmethods are not supported")
+                raise AnnotatorError("classmethods are not supported")
             s_value = self.bookkeeper.immutablevalue(value)
             if classdef is not None:
                 s_value = s_value.bind_callables_under(classdef, name)
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -676,7 +676,21 @@
 
 # ____________________________________________________________
 
-class UnionError(Exception):
+
+class AnnotatorError(Exception):
+    def __init__(self, msg=None):
+        self.msg = msg
+        self.source = None
+
+    def __str__(self):
+        s = "\n\n%s" % self.msg
+        if self.source is not None:
+            s += "\n\n"
+            s += self.source
+
+        return s
+
+class UnionError(AnnotatorError):
     """Signals an suspicious attempt at taking the union of
     deeply incompatible SomeXxx instances."""
 
@@ -686,25 +700,16 @@
         The msg paramter is appended to a generic message. This can be used to
         give the user a little more information.
         """
+        s = ""
+        if msg is not None:
+            s += "%s\n\n" % msg
+        s += "Offending annotations:\n"
+        s += "  %s\n  %s" % (s_obj1, s_obj2)
         self.s_obj1 = s_obj1
         self.s_obj2 = s_obj2
-        self.msg = msg
+        self.msg = s
         self.source = None
 
-    def __str__(self):
-        s = "\n\n"
-
-        if self.msg is not None:
-            s += "%s\n\n" % self.msg
-
-        s += "Offending annotations:\n"
-        s += "%s\n%s\n\n" % (self.s_obj1, self.s_obj2)
-
-        if self.source is not None:
-            s += self.source
-
-        return s
-
     def __repr__(self):
         return str(self)
 
diff --git a/rpython/annotator/test/test_annrpython.py b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -228,7 +228,7 @@
         def f():
             return X().meth()
         a = self.RPythonAnnotator()
-        py.test.raises(AssertionError, a.build_types, f,  [])
+        py.test.raises(annmodel.AnnotatorError, a.build_types, f,  [])
 
     def test_methodcall1(self):
         a = self.RPythonAnnotator()
@@ -3381,22 +3381,22 @@
             return '%s' % unichr(x)
 
         a = self.RPythonAnnotator()
-        py.test.raises(NotImplementedError, a.build_types, f, [int])
+        py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int])
         def f(x):
             return '%s' % (unichr(x) * 3)
 
         a = self.RPythonAnnotator()
-        py.test.raises(NotImplementedError, a.build_types, f, [int])
+        py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int])
         def f(x):
             return '%s%s' % (1, unichr(x))
 
         a = self.RPythonAnnotator()
-        py.test.raises(NotImplementedError, a.build_types, f, [int])
+        py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int])
         def f(x):
             return '%s%s' % (1, unichr(x) * 15)
 
         a = self.RPythonAnnotator()
-        py.test.raises(NotImplementedError, a.build_types, f, [int])
+        py.test.raises(annmodel.AnnotatorError, a.build_types, f, [int])
 
 
     def test_strformatting_tuple(self):
@@ -3434,7 +3434,7 @@
             return [1, 2, 3][s:e]
 
         a = self.RPythonAnnotator()
-        py.test.raises(TypeError, "a.build_types(f, [int, int])")
+        py.test.raises(annmodel.AnnotatorError, "a.build_types(f, [int, int])")
         a.build_types(f, [annmodel.SomeInteger(nonneg=True),
                           annmodel.SomeInteger(nonneg=True)])
         def f(x):
@@ -4038,7 +4038,6 @@
         s_objs = set([type(the_exc.s_obj1), type(the_exc.s_obj2)])
 
         assert s_objs == set([annmodel.SomeInteger, annmodel.SomeString])
-        assert the_exc.msg == None # Check that this is a generic UnionError
 
     def test_unionerror_tuple_size(self):
         def f(x):
@@ -4051,7 +4050,7 @@
         with py.test.raises(annmodel.UnionError) as exc:
             a.build_types(f, [int])
 
-        assert exc.value.msg == "RPython cannot unify tuples of different length: 2 versus 1"
+        assert "RPython cannot unify tuples of different length: 2 versus 1" in exc.value.msg
 
     def test_unionerror_signedness(self):
         def f(x):
@@ -4064,8 +4063,8 @@
         with py.test.raises(annmodel.UnionError) as exc:
             a.build_types(f, [int])
 
-        assert exc.value.msg == ("RPython cannot prove that these integers are of "
-                "the same signedness")
+        assert ("RPython cannot prove that these integers are of the "
+                "same signedness" in exc.value.msg)
 
     def test_unionerror_instance(self):
         class A(object): pass
@@ -4081,7 +4080,8 @@
         with py.test.raises(annmodel.UnionError) as exc:
             a.build_types(f, [int])
 
-        assert exc.value.msg == ("RPython cannot unify instances with no common base class")
+        assert ("RPython cannot unify instances with no common base class" 
+                in exc.value.msg)
 
     def test_unionerror_iters(self):
 
@@ -4096,7 +4096,27 @@
         with py.test.raises(annmodel.UnionError) as exc:
             a.build_types(f, [int])
 
-        assert exc.value.msg == ("RPython cannot unify incompatible iterator variants")
+        assert ("RPython cannot unify incompatible iterator variants" in 
+                exc.value.msg)
+
+    def test_variable_getattr(self):
+        class A(object): pass
+        def f(y):
+            a = A()
+            return getattr(a, y)
+        a = self.RPythonAnnotator()
+        with py.test.raises(annmodel.AnnotatorError) as exc:
+            a.build_types(f, [str])
+        assert ("variable argument to getattr" in exc.value.msg)
+
+    def test_bad_call(self):
+        def f(x):
+            return x()
+        a = self.RPythonAnnotator()
+        with py.test.raises(annmodel.AnnotatorError) as exc:
+            a.build_types(f, [str])
+        assert ("Cannot prove that the object is callable" in exc.value.msg)
+
 
 def g(n):
     return [0, 1, 2, n]
diff --git a/rpython/annotator/test/test_argument.py b/rpython/annotator/test/test_argument.py
--- a/rpython/annotator/test/test_argument.py
+++ b/rpython/annotator/test/test_argument.py
@@ -77,10 +77,6 @@
         new_args = args.unmatch_signature(sig, data)
         assert args.unpack() == new_args.unpack()
 
-        args = make_arguments_for_translation(space, [1], {'c': 5, 'd': 7})
-        sig = Signature(['a', 'b', 'c'], None, 'kw')
-        py.test.raises(TypeError, args.match_signature, sig, [2, 3])
-
     def test_rawshape(self):
         space = DummySpace()
         args = make_arguments_for_translation(space, [1,2,3])
diff --git a/rpython/annotator/test/test_model.py b/rpython/annotator/test/test_model.py
--- a/rpython/annotator/test/test_model.py
+++ b/rpython/annotator/test/test_model.py
@@ -2,6 +2,7 @@
 
 from rpython.annotator.model import *
 from rpython.annotator.listdef import ListDef
+from rpython.translator.translator import TranslationContext
 
 
 listdef1 = ListDef(None, SomeTuple([SomeInteger(nonneg=True), SomeString()]))
@@ -174,6 +175,28 @@
     assert f2.contains(f1)
     assert f1.contains(f2)
 
+def compile_function(function, annotation=[]):
+    t = TranslationContext()
+    t.buildannotator().build_types(function, annotation)
+
+class AAA(object):
+    pass
+
+def test_blocked_inference1():
+    def blocked_inference():
+        return AAA().m()
+
+    py.test.raises(AnnotatorError, compile_function, blocked_inference)
+
+def test_blocked_inference2():
+    def blocked_inference():
+        a = AAA()
+        b = a.x
+        return b
+
+    py.test.raises(AnnotatorError, compile_function, blocked_inference)
+
+
 if __name__ == '__main__':
     for name, value in globals().items():
         if name.startswith('test_'):
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -14,7 +14,7 @@
 from rpython.annotator.bookkeeper import getbookkeeper
 from rpython.annotator import builtin
 from rpython.annotator.binaryop import _clone ## XXX where to put this?
-from rpython.tool.error import AnnotatorError
+from rpython.annotator.model import AnnotatorError
 
 # convenience only!
 def immutablevalue(x):
@@ -84,8 +84,7 @@
         return obj.is_true()
 
     def hash(obj):
-        raise TypeError, ("cannot use hash() in RPython; "
-                          "see objectmodel.compute_xxx()")
+        raise AnnotatorError("cannot use hash() in RPython")
 
     def str(obj):
         getbookkeeper().count('str', obj)
@@ -158,9 +157,7 @@
         return obj.call(getbookkeeper().build_args("call_args", args_s))
 
     def call(obj, args, implicit_init=False):
-        #raise Exception, "cannot follow call_args%r" % ((obj, args),)
-        getbookkeeper().warning("cannot follow call(%r, %r)" % (obj, args))
-        return SomeObject()
+        raise AnnotatorError("Cannot prove that the object is callable")
 
     def op_contains(obj, s_element):
         return s_Bool
@@ -341,10 +338,10 @@
 
 def check_negative_slice(s_start, s_stop):
     if isinstance(s_start, SomeInteger) and not s_start.nonneg:
-        raise TypeError("slicing: not proven to have non-negative start")
+        raise AnnotatorError("slicing: not proven to have non-negative start")
     if isinstance(s_stop, SomeInteger) and not s_stop.nonneg and \
            getattr(s_stop, 'const', 0) != -1:
-        raise TypeError("slicing: not proven to have non-negative stop")
+        raise AnnotatorError("slicing: not proven to have non-negative stop")
 
 
 class __extend__(SomeDict):
@@ -529,10 +526,10 @@
 class __extend__(SomeUnicodeString):
     def method_encode(uni, s_enc):
         if not s_enc.is_constant():
-            raise TypeError("Non-constant encoding not supported")
+            raise AnnotatorError("Non-constant encoding not supported")
         enc = s_enc.const
         if enc not in ('ascii', 'latin-1', 'utf-8'):
-            raise TypeError("Encoding %s not supported for unicode" % (enc,))
+            raise AnnotatorError("Encoding %s not supported for unicode" % (enc,))
         return SomeString()
     method_encode.can_only_throw = [UnicodeEncodeError]
 
@@ -562,10 +559,10 @@
 
     def method_decode(str, s_enc):
         if not s_enc.is_constant():
-            raise TypeError("Non-constant encoding not supported")
+            raise AnnotatorError("Non-constant encoding not supported")
         enc = s_enc.const
         if enc not in ('ascii', 'latin-1', 'utf-8'):
-            raise TypeError("Encoding %s not supported for strings" % (enc,))
+            raise AnnotatorError("Encoding %s not supported for strings" % (enc,))
         return SomeUnicodeString()
     method_decode.can_only_throw = [UnicodeDecodeError]
 
@@ -653,7 +650,7 @@
         if s_attr.is_constant() and isinstance(s_attr.const, str):
             attr = s_attr.const
             return ins._true_getattr(attr)
-        return SomeObject()
+        raise AnnotatorError("A variable argument to getattr is not RPython")
     getattr.can_only_throw = []
 
     def setattr(ins, s_attr, s_value):
@@ -729,7 +726,7 @@
 
     def setattr(pbc, s_attr, s_value):
         if not pbc.isNone():
-            raise AnnotatorError("setattr on %r" % pbc)
+            raise AnnotatorError("Cannot modify attribute of a pre-built constant")
 
     def call(pbc, args):
         bookkeeper = getbookkeeper()
@@ -751,7 +748,8 @@
             # whose length is the constant 0; so let's tentatively answer 0.
             return immutablevalue(0)
         else:
-            return SomeObject()    # len() on a pbc? no chance
+            # This should probably never happen
+            raise AnnotatorError("Cannot call len on a pbc")
 
 # annotation of low-level types
 from rpython.annotator.model import SomePtr, SomeLLADTMeth
diff --git a/rpython/tool/error.py b/rpython/tool/error.py
--- a/rpython/tool/error.py
+++ b/rpython/tool/error.py
@@ -68,19 +68,6 @@
     lines = source_lines1(graph, *args, **kwds)
     return ['In %r:' % (graph,)] + lines
 
-class AnnotatorError(Exception):
-    pass
-
-class NoSuchAttrError(Exception):
-    pass
-
-class ErrorWrapper(object):
-    def __init__(self, msg):
-        self.msg = msg
-
-    def __repr__(self):
-        return '<%s>' % (self.msg,)
-
 def gather_error(annotator, graph, block, operindex):
     msg = [""]
 
@@ -94,19 +81,24 @@
     msg += source_lines(graph, block, operindex, long=True)
     if oper is not None:
         if SHOW_ANNOTATIONS:
-            msg.append("Known variable annotations:")
-            for arg in oper.args + [oper.result]:
-                if isinstance(arg, Variable):
-                    try:
-                        msg.append(" " + str(arg) + " = " + str(annotator.binding(arg)))
-                    except KeyError:
-                        pass
+            msg += format_annotations(annotator, oper)
+            msg += ['']
     return "\n".join(msg)
 
+def format_annotations(annotator, oper):
+    msg = []
+    msg.append("Known variable annotations:")
+    for arg in oper.args + [oper.result]:
+        if isinstance(arg, Variable):
+            try:
+                msg.append(" " + str(arg) + " = " + str(annotator.binding(arg)))
+            except KeyError:
+                pass
+    return msg
+
 def format_blocked_annotation_error(annotator, blocked_blocks):
     text = []
     for block, (graph, index) in blocked_blocks.items():
-        text.append('\n')
         text.append("Blocked block -- operation cannot succeed")
         text.append(gather_error(annotator, graph, block, index))
     return '\n'.join(text)
diff --git a/rpython/tool/test/test_error.py b/rpython/tool/test/test_error.py
--- a/rpython/tool/test/test_error.py
+++ b/rpython/tool/test/test_error.py
@@ -3,33 +3,10 @@
 """
 
 from rpython.translator.translator import TranslationContext
-from rpython.tool.error import AnnotatorError
 from rpython.annotator.model import UnionError
 
 import py
 
-
-def compile_function(function, annotation=[]):
-    t = TranslationContext()
-    t.buildannotator().build_types(function, annotation)
-
-class AAA(object):
-    pass
-
-def test_blocked_inference1():
-    def blocked_inference():
-        return AAA().m()
-
-    py.test.raises(AnnotatorError, compile_function, blocked_inference)
-
-def test_blocked_inference2():
-    def blocked_inference():
-        a = AAA()
-        b = a.x
-        return b
-
-    py.test.raises(AnnotatorError, compile_function, blocked_inference)
-
 def test_someobject():
     def someobject_degeneration(n):
         if n == 3:


More information about the pypy-commit mailing list