From 96a17b1683fb1bd5b49abff2e4e661084268e672 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 11 Sep 2020 16:44:24 -0700 Subject: [PATCH] Fix INTERNALERROR when accessing locals / globals with faulty `exec` --- changelog/7742.bugfix.rst | 1 + src/_pytest/_code/code.py | 16 +++++++++++++--- testing/code/test_excinfo.py | 13 +++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 changelog/7742.bugfix.rst diff --git a/changelog/7742.bugfix.rst b/changelog/7742.bugfix.rst new file mode 100644 index 00000000000..95277ee8908 --- /dev/null +++ b/changelog/7742.bugfix.rst @@ -0,0 +1 @@ +Fix INTERNALERROR when accessing locals / globals with faulty ``exec``. diff --git a/src/_pytest/_code/code.py b/src/_pytest/_code/code.py index 12d39306a69..98aea8c11ec 100644 --- a/src/_pytest/_code/code.py +++ b/src/_pytest/_code/code.py @@ -246,10 +246,20 @@ def ishidden(self) -> bool: Mostly for internal use. """ - f = self.frame - tbh = f.f_locals.get( - "__tracebackhide__", f.f_globals.get("__tracebackhide__", False) + tbh = ( + False ) # type: Union[bool, Callable[[Optional[ExceptionInfo[BaseException]]], bool]] + for maybe_ns_dct in (self.frame.f_locals, self.frame.f_globals): + # in normal cases, f_locals and f_globals are dictionaries + # however via `exec(...)` / `eval(...)` they can be other types + # (even incorrect types!). + # as such, we suppress all exceptions while accessing __tracebackhide__ + try: + tbh = maybe_ns_dct["__tracebackhide__"] + except Exception: + pass + else: + break if tbh and callable(tbh): return tbh(None if self._excinfo is None else self._excinfo()) return tbh diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 4dfd6f5cc95..5754977ddc7 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -1352,6 +1352,19 @@ def unreraise(): ) assert out == expected_out + def test_exec_type_error_filter(self, importasmod): + """See #7742""" + mod = importasmod( + """\ + def f(): + exec("a = 1", {}, []) + """ + ) + with pytest.raises(TypeError) as excinfo: + mod.f() + # previously crashed with `AttributeError: list has no attribute get` + excinfo.traceback.filter() + @pytest.mark.parametrize("style", ["short", "long"]) @pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])