Closed
Description
Bug report
The exception urllib.error.HTTPError(..., fp=None)
raises a KeyError
instead of an AttributeError
when accessing an attribute that does not exist.
>>> from urllib.error import HTTPError >>> x = HTTPError("url", 405, "METHOD NOT ALLOWED", None, None) >>> assert getattr(x, "__notes__", ()) == () Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/vinmic/repos/cpython/Lib/tempfile.py", line 477, in __getattr__ file = self.__dict__['file'] ~~~~~~~~~~~~~^^^^^^^^ KeyError: 'file'
Your environment
Python 3.12.0a1+ (heads/main:bded5edd9a, Oct 27 2022, 19:23:58) [GCC 11.3.0] on linux
This bug should be reproducible for all python3 versions on all systems.
Context
I found this error while running a code similar to this:
from logging import getLogger from urllib.error import HTTPError try: raise HTTPError("url", 405, "METHOD NOT ALLOWED", None, None) except Exception: getLogger().exception("Ooops")
Instead of having the exception logged, I ended up with the following trace:
--- Logging error --- Traceback (most recent call last): File "/home/vinmic/repos/cpython/../test_trio/test_trio.py", line 6, in <module> raise HTTPError("url", 405, "METHOD NOT ALLOWED", None, None) urllib.error.HTTPError: HTTP Error 405: METHOD NOT ALLOWED During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1160, in emit msg = self.format(record) ^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 999, in format return fmt.format(record) ^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 711, in format record.exc_text = self.formatException(record.exc_info) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 661, in formatException traceback.print_exception(ei[0], ei[1], tb, None, sio) File "/home/vinmic/repos/cpython/Lib/traceback.py", line 124, in print_exception te = TracebackException(type(value), value, tb, limit=limit, compact=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/traceback.py", line 697, in __init__ self.__notes__ = getattr(exc_value, '__notes__', None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/tempfile.py", line 477, in __getattr__ file = self.__dict__['file'] ~~~~~~~~~~~~~^^^^^^^^ KeyError: 'file' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/vinmic/repos/cpython/../test_trio/test_trio.py", line 8, in <module> getLogger().exception("Ooops") File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1574, in exception self.error(msg, *args, exc_info=exc_info, **kwargs) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1568, in error self._log(ERROR, msg, args, **kwargs) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1684, in _log self.handle(record) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1700, in handle self.callHandlers(record) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1770, in callHandlers lastResort.handle(record) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1028, in handle self.emit(record) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1168, in emit self.handleError(record) File "/home/vinmic/repos/cpython/Lib/logging/__init__.py", line 1082, in handleError traceback.print_exception(t, v, tb, None, sys.stderr) File "/home/vinmic/repos/cpython/Lib/traceback.py", line 124, in print_exception te = TracebackException(type(value), value, tb, limit=limit, compact=True) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/traceback.py", line 763, in __init__ context = TracebackException( ^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/traceback.py", line 697, in __init__ self.__notes__ = getattr(exc_value, '__notes__', None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/vinmic/repos/cpython/Lib/tempfile.py", line 477, in __getattr__ file = self.__dict__['file'] ~~~~~~~~~~~~~^^^^^^^^ KeyError: 'file'
Note however that the exception is logged properly on python 3.9 (without the exceptiongroup
module imported). So maybe the following patch should be applied on top of HTTPError
being fixed in order to make tracebacks more robust:
diff --git a/Lib/traceback.py b/Lib/traceback.py index 6270100348..a9c15d59be 100644 --- a/Lib/traceback.py +++ b/Lib/traceback.py @@ -694,7 +694,10 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, # Capture now to permit freeing resources: only complication is in the # unofficial API _format_final_exc_line self._str = _safe_string(exc_value, 'exception') - self.__notes__ = getattr(exc_value, '__notes__', None) + try: + self.__notes__ = getattr(exc_value, '__notes__', None) + except Exception: + self.__notes__ = None if exc_type and issubclass(exc_type, SyntaxError): # Handle SyntaxError's specially