[pypy-commit] pypy py3.5: Don't wrap codec exceptions when they have an attribute or more arguments.

amauryfa pypy.commits at gmail.com
Sat Nov 5 16:23:25 EDT 2016


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: py3.5
Changeset: r88148:9abb6079ac04
Date: 2016-11-05 17:27 +0100
http://bitbucket.org/pypy/pypy/changeset/9abb6079ac04/

Log:	Don't wrap codec exceptions when they have an attribute or more
	arguments.

	Move the code to errors.py because it relies on implementation
	details.

diff --git a/pypy/interpreter/error.py b/pypy/interpreter/error.py
--- a/pypy/interpreter/error.py
+++ b/pypy/interpreter/error.py
@@ -344,6 +344,42 @@
         finally:
             self._context_recorded = True
 
+    # A simplified version of _PyErr_TrySetFromCause, which returns a
+    # new exception of the same class, but with another error message.
+    # This only works for exceptions which have just a single message,
+    # and no other attribute.
+    # Otherwise the same OperationError is returned.
+    def try_set_from_cause(self, space, message):
+        from pypy.module.exceptions.interp_exceptions import W_BaseException
+        self.normalize_exception(space)
+        w_value = self.get_w_value(space)
+        if not isinstance(w_value, W_BaseException):
+            return self
+        exc = w_value
+        # "args" should be empty or contain a single string
+        if len(exc.args_w) == 0:
+            pass
+        elif len(exc.args_w) == 1:
+            if not space.isinstance_w(exc.args_w[0], space.w_unicode):
+                return self
+        else:
+            return self
+        # No instance attribute.
+        if exc.w_dict and space.is_true(exc.w_dict):
+            return self
+        # Try to create the new exception.
+        try:
+            new_error = oefmt(space.type(w_value),
+                              "%s (%T: %S)", message, w_value, w_value)
+            new_error.w_cause = w_value
+            new_error.normalize_exception(space)
+            # Copy the traceback, but it does not escape.
+            new_error.set_traceback(self._application_traceback)
+        except OperationError:
+            # Return the original error
+            return self
+        return new_error
+
 
 def _break_context_cycle(space, w_value, w_context):
     """Break reference cycles in the __context__ chain.
diff --git a/pypy/module/_codecs/interp_codecs.py b/pypy/module/_codecs/interp_codecs.py
--- a/pypy/module/_codecs/interp_codecs.py
+++ b/pypy/module/_codecs/interp_codecs.py
@@ -415,23 +415,11 @@
         state.codec_error_registry[error] = space.wrap(interp2app(globals()[name]))
 
 
-# A simplified version of the incredibly complex CPython function
-# _PyErr_TrySetFromCause, which returns a new exception with another
-# error message.  Subclasses of UnicodeErrors are returned inchanged,
-# but this is only a side-effect: they cannot be constructed with a
-# simple message.
 def _wrap_codec_error(space, operr, action, encoding):
-    w_exc = operr.get_w_value(space)
-    try:
-        new_operr = oefmt(space.type(w_exc),
-                          "%s with '%s' codec failed (%T: %S)",
-                          action, encoding, w_exc, w_exc)
-        new_operr.w_cause = w_exc
-        new_operr.normalize_exception(space)
-    except OperationError:
-        # Return the original error
-        return operr
-    return new_operr
+    # Note that UnicodeErrors are not wrapped and returned as is,
+    # "thanks to" a limitation of try_set_from_cause.
+    message = "%s with '%s' codec failed" % (action, encoding)
+    return operr.try_set_from_cause(space, message)
 
 def _call_codec(space, w_decoder, w_obj, action, encoding, errors):
     try:
diff --git a/pypy/module/_codecs/test/test_codecs.py b/pypy/module/_codecs/test/test_codecs.py
--- a/pypy/module/_codecs/test/test_codecs.py
+++ b/pypy/module/_codecs/test/test_codecs.py
@@ -380,11 +380,12 @@
         import _codecs
         def search_function(encoding):
             def f(input, errors="strict"):
-                raise RuntimeError('should be wrapped')
+                raise to_raise
             if encoding == 'test.failingenc':
                 return (f, f, None, None)
             return None
         _codecs.register(search_function)
+        to_raise = RuntimeError('should be wrapped')
         exc = raises(RuntimeError, b"hello".decode, "test.failingenc")
         assert str(exc.value) == (
             "decoding with 'test.failingenc' codec failed "
@@ -393,6 +394,14 @@
         assert str(exc.value) == (
             "encoding with 'test.failingenc' codec failed "
             "(RuntimeError: should be wrapped)")
+        #
+        to_raise.attr = "don't wrap"
+        exc = raises(RuntimeError, u"hello".encode, "test.failingenc")
+        assert exc.value == to_raise
+        #
+        to_raise = RuntimeError("Should", "Not", "Wrap")
+        exc = raises(RuntimeError, u"hello".encode, "test.failingenc")
+        assert exc.value == to_raise
 
     def test_cpytest_decode(self):
         import codecs


More information about the pypy-commit mailing list