Skip to content

Fix exception causes all over the codebase #7387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ Pulkit Goyal
Punyashloka Biswal
Quentin Pradet
Ralf Schmitt
Ram Rachum
Ralph Giles
Ran Benita
Raphael Castaneda
Expand Down
1 change: 1 addition & 0 deletions changelog/7383.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed exception causes all over the codebase, i.e. use `raise new_exception from old_exception` when wrapping an exception.
2 changes: 1 addition & 1 deletion src/_pytest/_code/source.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ def compile( # noqa: F811
newex.offset = ex.offset
newex.lineno = ex.lineno
newex.text = ex.text
raise newex
raise newex from ex
else:
if flag & ast.PyCF_ONLY_AST:
assert isinstance(co, ast.AST)
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1189,8 +1189,8 @@ def getini(self, name: str):
def _getini(self, name: str) -> Any:
try:
description, type, default = self._parser._inidict[name]
except KeyError:
raise ValueError("unknown configuration value: {!r}".format(name))
except KeyError as e:
raise ValueError("unknown configuration value: {!r}".format(name)) from e
override_value = self._get_override_ini_value(name)
if override_value is None:
try:
Expand Down Expand Up @@ -1286,14 +1286,14 @@ def getoption(self, name: str, default=notset, skip: bool = False):
if val is None and skip:
raise AttributeError(name)
return val
except AttributeError:
except AttributeError as e:
if default is not notset:
return default
if skip:
import pytest

pytest.skip("no {!r} option found".format(name))
raise ValueError("no option named {!r}".format(name))
raise ValueError("no option named {!r}".format(name)) from e

def getvalue(self, name, path=None):
""" (deprecated, use getoption()) """
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/config/argparsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,9 +265,9 @@ def __init__(self, *names: str, **attrs: Any) -> None:
else:
try:
self.dest = self._short_opts[0][1:]
except IndexError:
except IndexError as e:
self.dest = "???" # Needed for the error repr.
raise ArgumentError("need a long or short option", self)
raise ArgumentError("need a long or short option", self) from e

def names(self) -> List[str]:
return self._short_opts + self._long_opts
Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/config/findpaths.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def _parse_ini_config(path: py.path.local) -> iniconfig.IniConfig:
try:
return iniconfig.IniConfig(path)
except iniconfig.ParseError as exc:
raise UsageError(str(exc))
raise UsageError(str(exc)) from exc


def load_config_dict_from_file(
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/debugging.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ def _validate_usepdb_cls(value: str) -> Tuple[str, str]:
"""Validate syntax of --pdbcls option."""
try:
modname, classname = value.split(":")
except ValueError:
except ValueError as e:
raise argparse.ArgumentTypeError(
"{!r} is not in the format 'modname:classname'".format(value)
)
) from e
return (modname, classname)


Expand Down Expand Up @@ -130,7 +130,7 @@ def _import_pdb_cls(cls, capman: "CaptureManager"):
value = ":".join((modname, classname))
raise UsageError(
"--pdbcls: could not import {!r}: {}".format(value, exc)
)
) from exc
else:
import pdb

Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -938,13 +938,13 @@ def _eval_scope_callable(
# Type ignored because there is no typing mechanism to specify
# keyword arguments, currently.
result = scope_callable(fixture_name=fixture_name, config=config) # type: ignore[call-arg] # noqa: F821
except Exception:
except Exception as e:
raise TypeError(
"Error evaluating {} while defining fixture '{}'.\n"
"Expected a function with the signature (*, fixture_name, config)".format(
scope_callable, fixture_name
)
)
) from e
if not isinstance(result, str):
fail(
"Expected {} to return a 'str' while defining fixture '{}', but it returned:\n"
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,13 +487,13 @@ def get_log_level_for_setting(config: Config, *setting_names: str) -> Optional[i
log_level = log_level.upper()
try:
return int(getattr(logging, log_level, log_level))
except ValueError:
except ValueError as e:
# Python logging does not recognise this as a logging level
raise pytest.UsageError(
"'{}' is not recognized as a logging level name for "
"'{}'. Please consider passing the "
"logging level num instead.".format(log_level, setting_name)
)
) from e


# run after terminalreporter/capturemanager are configured
Expand Down
6 changes: 3 additions & 3 deletions src/_pytest/monkeypatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,20 @@ def resolve(name: str) -> object:
if expected == used:
raise
else:
raise ImportError("import error in {}: {}".format(used, ex))
raise ImportError("import error in {}: {}".format(used, ex)) from ex
found = annotated_getattr(found, part, used)
return found


def annotated_getattr(obj: object, name: str, ann: str) -> object:
try:
obj = getattr(obj, name)
except AttributeError:
except AttributeError as e:
raise AttributeError(
"{!r} object at {} has no attribute {!r}".format(
type(obj).__name__, ann, name
)
)
) from e
return obj


Expand Down
22 changes: 12 additions & 10 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,8 +551,10 @@ def _importtestmodule(self):
importmode = self.config.getoption("--import-mode")
try:
mod = import_path(self.fspath, mode=importmode)
except SyntaxError:
raise self.CollectError(ExceptionInfo.from_current().getrepr(style="short"))
except SyntaxError as e:
raise self.CollectError(
ExceptionInfo.from_current().getrepr(style="short")
) from e
except ImportPathMismatchError as e:
raise self.CollectError(
"import file mismatch:\n"
Expand All @@ -562,8 +564,8 @@ def _importtestmodule(self):
" %s\n"
"HINT: remove __pycache__ / .pyc files and/or use a "
"unique basename for your test file modules" % e.args
)
except ImportError:
) from e
except ImportError as e:
exc_info = ExceptionInfo.from_current()
if self.config.getoption("verbose") < 2:
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
Expand All @@ -578,7 +580,7 @@ def _importtestmodule(self):
"Hint: make sure your test modules/packages have valid Python names.\n"
"Traceback:\n"
"{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
)
) from e
except _pytest.runner.Skipped as e:
if e.allow_module_level:
raise
Expand All @@ -587,7 +589,7 @@ def _importtestmodule(self):
"To decorate a test function, use the @pytest.mark.skip "
"or @pytest.mark.skipif decorators instead, and to skip a "
"module use `pytestmark = pytest.mark.{skip,skipif}."
)
) from e
self.config.pluginmanager.consider_module(mod)
return mod

Expand Down Expand Up @@ -836,8 +838,8 @@ def _checkargnotcontained(self, arg: str) -> None:
def getparam(self, name: str) -> object:
try:
return self.params[name]
except KeyError:
raise ValueError(name)
except KeyError as e:
raise ValueError(name) from e

@property
def id(self) -> str:
Expand Down Expand Up @@ -1074,8 +1076,8 @@ def _validate_ids(
except TypeError:
try:
iter(ids)
except TypeError:
raise TypeError("ids must be a callable or an iterable")
except TypeError as e:
raise TypeError("ids must be a callable or an iterable") from e
num_ids = len(parameters)

# num_ids == 0 is a special case: https://github.com/pytest-dev/pytest/issues/1849
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ def _parse_filter(
lineno = int(lineno_)
if lineno < 0:
raise ValueError
except (ValueError, OverflowError):
raise warnings._OptionError("invalid lineno {!r}".format(lineno_))
except (ValueError, OverflowError) as e:
raise warnings._OptionError("invalid lineno {!r}".format(lineno_)) from e
else:
lineno = 0
return (action, message, category, module, lineno)
Expand Down
4 changes: 2 additions & 2 deletions testing/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1778,5 +1778,5 @@ def test_conftest_import_error_repr(tmpdir):
):
try:
raise RuntimeError("some error")
except Exception:
raise ConftestImportFailure(path, sys.exc_info())
except Exception as e:
raise ConftestImportFailure(path, sys.exc_info()) from e
4 changes: 2 additions & 2 deletions testing/test_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,8 +534,8 @@ def test_outcomeexception_passes_except_Exception() -> None:
with pytest.raises(outcomes.OutcomeException):
try:
raise outcomes.OutcomeException("test")
except Exception:
raise NotImplementedError()
except Exception as e:
raise NotImplementedError from e


def test_pytest_exit() -> None:
Expand Down