summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMaurits van Rees <maurits@vanrees.org>2022-11-08 14:29:53 +0100
committerMaurits van Rees <maurits@vanrees.org>2022-11-08 14:36:44 +0100
commitb7c4196c21b2fe7a2f7b307e1d247c999be1090a (patch)
treea3ef281314aa2084a889c248280bd47c8b2f88c1
parent594eee0457cba5d2b93920de15f8f2c6399dd34d (diff)
downloadzope-exceptions-b7c4196c21b2fe7a2f7b307e1d247c999be1090a.tar.gz
Catch exceptions in formatExceptionOnly.
Getting an exception when reporting about a different exception is not helpful. On Python 3.11 this is needed for some HTTPErrors.
-rw-r--r--CHANGES.rst4
-rw-r--r--src/zope/exceptions/exceptionformatter.py9
-rw-r--r--src/zope/exceptions/tests/test_exceptionformatter.py41
3 files changed, 53 insertions, 1 deletions
diff --git a/CHANGES.rst b/CHANGES.rst
index 88f6e1a..5d3521b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -5,6 +5,10 @@
4.6 (unreleased)
================
+- Catch exceptions in ``formatExceptionOnly``.
+ Getting an exception when reporting about a different exception is not helpful.
+ On Python 3.11 this is needed for some HTTPErrors.
+
- Add official support for Python 3.11.
diff --git a/src/zope/exceptions/exceptionformatter.py b/src/zope/exceptions/exceptionformatter.py
index e6b0314..b61026c 100644
--- a/src/zope/exceptions/exceptionformatter.py
+++ b/src/zope/exceptions/exceptionformatter.py
@@ -171,7 +171,14 @@ class TextExceptionFormatter(object):
return self.line_sep.join(result)
def formatExceptionOnly(self, etype, value):
- result = ''.join(traceback.format_exception_only(etype, value))
+ # We don't want to get an error when we format an error, so we
+ # compensate in our code. For example, on Python 3.11.0 HTTPError
+ # gives an unhelpful KeyError in tempfile when Python formats it.
+ # See https://github.com/python/cpython/issues/90113
+ try:
+ result = ''.join(traceback.format_exception_only(etype, value))
+ except Exception:
+ result = str(value)
return result
def formatLastLine(self, exc_line):
diff --git a/src/zope/exceptions/tests/test_exceptionformatter.py b/src/zope/exceptions/tests/test_exceptionformatter.py
index 46eea6d..4d6c6fc 100644
--- a/src/zope/exceptions/tests/test_exceptionformatter.py
+++ b/src/zope/exceptions/tests/test_exceptionformatter.py
@@ -278,6 +278,47 @@ class TextExceptionFormatterTests(unittest.TestCase):
''.join(
traceback.format_exception_only(ValueError, err)))
+ def test_formatExceptionOnly_httperror(self):
+ # On Python 3.11.0 HTTPError may behave wrongly, giving a KeyError in
+ # tempfile when Python tries to format it.
+ # See https://github.com/python/cpython/issues/90113
+ # or examples in Plone tests, especially doctests:
+ # https://github.com/plone/Products.CMFPlone/issues/3663
+ # We don't want to get an error when we format an error,
+ # so let's compensate in our code.
+ try:
+ from urllib.error import HTTPError
+ except ImportError:
+ # BBB for Python 2.7
+ from urllib2 import HTTPError
+ fmt = self._makeOne()
+ err = HTTPError('url', 400, 'oops', [], None)
+ result = fmt.formatExceptionOnly(HTTPError, err).strip()
+ # The output can differ too much per Python version,
+ # but it is just one line when stripped.
+ self.assertIn("400", result)
+ self.assertIn("oops", result)
+ self.assertIn("Error", result)
+ self.assertEqual(len(result.splitlines()), 1)
+
+ def test_formatException_httperror(self):
+ # See test_formatExceptionOnly_httperror.
+ # Here we check that formatException works.
+ try:
+ from urllib.error import HTTPError
+ except ImportError:
+ # BBB for Python 2.7
+ from urllib2 import HTTPError
+ fmt = self._makeOne()
+ err = HTTPError('url', 400, 'oops', [], None)
+ result = fmt.formatException(HTTPError, err, None)
+ self.assertEqual(result[0], 'Traceback (most recent call last):\n')
+ last = result[-1]
+ # The output can differ per Python version.
+ self.assertIn("400", last)
+ self.assertIn("oops", last)
+ self.assertIn("Error", last)
+
def test_formatLastLine(self):
fmt = self._makeOne()
self.assertEqual(fmt.formatLastLine('XXX'), 'XXX')