From 978b315861c553618fd7ec813e21f3e3916f0d09 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 6 Mar 2020 15:25:35 +0200 Subject: [PATCH] Simplify some exception handling code Mostly avoid unnecessary usage of sys.exc_info(). Since Python3, exception objects themselves have all that's needed. They are also easier to type. --- src/_pytest/config/__init__.py | 10 +++----- src/_pytest/fixtures.py | 16 ++++++------- src/_pytest/main.py | 4 ++-- src/_pytest/python.py | 3 +-- src/_pytest/runner.py | 42 +++++++++++++++------------------- 5 files changed, 32 insertions(+), 43 deletions(-) diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index f499a349a..e21b9f1e2 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -612,13 +612,9 @@ class PytestPluginManager(PluginManager): try: __import__(importspec) except ImportError as e: - new_exc_message = 'Error importing plugin "{}": {}'.format( - modname, str(e.args[0]) - ) - new_exc = ImportError(new_exc_message) - tb = sys.exc_info()[2] - - raise new_exc.with_traceback(tb) + raise ImportError( + 'Error importing plugin "{}": {}'.format(modname, str(e.args[0])) + ).with_traceback(e.__traceback__) except Skipped as e: from _pytest.warnings import _issue_warning_captured diff --git a/src/_pytest/fixtures.py b/src/_pytest/fixtures.py index cdd249d93..34671656e 100644 --- a/src/_pytest/fixtures.py +++ b/src/_pytest/fixtures.py @@ -862,19 +862,19 @@ class FixtureDef: self._finalizers.append(finalizer) def finish(self, request): - exceptions = [] + exc = None try: while self._finalizers: try: func = self._finalizers.pop() func() - except: # noqa - exceptions.append(sys.exc_info()) - if exceptions: - _, val, tb = exceptions[0] - # Ensure to not keep frame references through traceback. - del exceptions - raise val.with_traceback(tb) + except BaseException as e: + # XXX Only first exception will be seen by user, + # ideally all should be reported. + if exc is None: + exc = e + if exc: + raise exc finally: hook = self._fixturemanager.session.gethookproxy(request.node.fspath) hook.pytest_fixture_post_finalizer(fixturedef=self, request=request) diff --git a/src/_pytest/main.py b/src/_pytest/main.py index 21a6b648c..61eb7ca74 100644 --- a/src/_pytest/main.py +++ b/src/_pytest/main.py @@ -496,11 +496,11 @@ class Session(nodes.FSCollector): self.trace.root.indent += 1 try: yield from self._collect(fspath, parts) - except NoMatch: + except NoMatch as exc: report_arg = "::".join((str(fspath), *parts)) # we are inside a make_report hook so # we cannot directly pass through the exception - self._notfound.append((report_arg, sys.exc_info()[1])) + self._notfound.append((report_arg, exc)) self.trace.root.indent -= 1 self._collection_node_cache1.clear() diff --git a/src/_pytest/python.py b/src/_pytest/python.py index bb2b43153..daa025f7f 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -516,8 +516,7 @@ class Module(nodes.File, PyCollector): mod = self.fspath.pyimport(ensuresyspath=importmode) except SyntaxError: raise self.CollectError(ExceptionInfo.from_current().getrepr(style="short")) - except self.fspath.ImportMismatchError: - e = sys.exc_info()[1] + except self.fspath.ImportMismatchError as e: raise self.CollectError( "import file mismatch:\n" "imported module %r has this __file__ attribute:\n" diff --git a/src/_pytest/runner.py b/src/_pytest/runner.py index 8455b2aee..412ea44a8 100644 --- a/src/_pytest/runner.py +++ b/src/_pytest/runner.py @@ -133,16 +133,14 @@ def pytest_runtest_call(item): pass try: item.runtest() - except Exception: + except Exception as e: # Store trace info to allow postmortem debugging - type, value, tb = sys.exc_info() - assert tb is not None - tb = tb.tb_next # Skip *this* frame - sys.last_type = type - sys.last_value = value - sys.last_traceback = tb - del type, value, tb # Get rid of these in this frame - raise + sys.last_type = type(e) + sys.last_value = e + assert e.__traceback__ is not None + # Skip *this* frame + sys.last_traceback = e.__traceback__.tb_next + raise e def pytest_runtest_teardown(item, nextitem): @@ -318,15 +316,13 @@ class SetupState: fin = finalizers.pop() try: fin() - except TEST_OUTCOME: + except TEST_OUTCOME as e: # XXX Only first exception will be seen by user, # ideally all should be reported. if exc is None: - exc = sys.exc_info() + exc = e if exc: - _, val, tb = exc - assert val is not None - raise val.with_traceback(tb) + raise exc def _teardown_with_finalization(self, colitem): self._callfinalizers(colitem) @@ -352,15 +348,13 @@ class SetupState: break try: self._pop_and_teardown() - except TEST_OUTCOME: + except TEST_OUTCOME as e: # XXX Only first exception will be seen by user, # ideally all should be reported. if exc is None: - exc = sys.exc_info() + exc = e if exc: - _, val, tb = exc - assert val is not None - raise val.with_traceback(tb) + raise exc def prepare(self, colitem): """ setup objects along the collector chain to the test-method @@ -371,15 +365,15 @@ class SetupState: # check if the last collection node has raised an error for col in self.stack: if hasattr(col, "_prepare_exc"): - _, val, tb = col._prepare_exc - raise val.with_traceback(tb) + exc = col._prepare_exc + raise exc for col in needed_collectors[len(self.stack) :]: self.stack.append(col) try: col.setup() - except TEST_OUTCOME: - col._prepare_exc = sys.exc_info() - raise + except TEST_OUTCOME as e: + col._prepare_exc = e + raise e def collect_one_node(collector):