Typed ExceptionGroupTypes and changed to BaseExceptionGroup, fixed exceptionchain (excinfo->excinfo_, set reprcrash. Extended tests, though they're wip.

This commit is contained in:
jakkdl 2022-08-12 16:28:46 +02:00
parent 078b112138
commit 163201e56b
4 changed files with 107 additions and 46 deletions

View File

@ -168,6 +168,7 @@ Jeff Rackauckas
Jeff Widman
Jenni Rinker
John Eddie Ayson
John Litborn
John Towler
Jon Parise
Jon Sonesen

View File

@ -56,17 +56,18 @@ if TYPE_CHECKING:
_TracebackStyle = Literal["long", "short", "line", "no", "native", "value", "auto"]
ExceptionGroupTypes: tuple = () # type: ignore
try:
ExceptionGroupTypes += (ExceptionGroup,) # type: ignore
except NameError:
pass # Is missing for `python<3.10`
ExceptionGroupTypes: Tuple[Type[BaseException], ...] = ()
if sys.version_info >= (3, 11):
ExceptionGroupTypes = (BaseExceptionGroup,) # type: ignore # noqa: F821
try:
import exceptiongroup
ExceptionGroupTypes += (exceptiongroup.ExceptionGroup,)
ExceptionGroupTypes += (exceptiongroup.BaseExceptionGroup,)
except ModuleNotFoundError:
pass # No backport is installed
# no backport installed - if <3.11 that means programs can't raise exceptiongroups
# so we don't need to handle it
pass
class Code:
@ -935,20 +936,22 @@ class FormattedExcinfo:
seen: Set[int] = set()
while e is not None and id(e) not in seen:
seen.add(id(e))
if isinstance(e, ExceptionGroupTypes):
reprtraceback: Union[
ReprTracebackNative, ReprTraceback
] = ReprTracebackNative(
traceback.format_exception(
type(excinfo.value),
excinfo.value,
excinfo.traceback[0]._rawentry,
if excinfo_:
if isinstance(e, tuple(ExceptionGroupTypes)):
reprtraceback: Union[
ReprTracebackNative, ReprTraceback
] = ReprTracebackNative(
traceback.format_exception(
type(excinfo_.value),
excinfo_.value,
excinfo_.traceback[0]._rawentry,
)
)
else:
reprtraceback = self.repr_traceback(excinfo_)
reprcrash: Optional[ReprFileLocation] = (
excinfo_._getreprcrash() if self.style != "value" else None
)
reprcrash: Optional[ReprFileLocation] = None
elif excinfo_:
reprtraceback = self.repr_traceback(excinfo_)
reprcrash = excinfo_._getreprcrash() if self.style != "value" else None
else:
# Fallback to native repr if the exception doesn't have a traceback:
# ExceptionInfo objects require a full traceback to work.

View File

@ -11,6 +11,11 @@ from typing import Tuple
from typing import TYPE_CHECKING
from typing import Union
try:
import exceptiongroup # noqa (referred to in strings)
except ModuleNotFoundError:
pass
import _pytest
import pytest
from _pytest._code.code import ExceptionChainRepr
@ -23,7 +28,6 @@ from _pytest.pathlib import import_path
from _pytest.pytester import LineMatcher
from _pytest.pytester import Pytester
if TYPE_CHECKING:
from _pytest._code.code import _TracebackStyle
@ -1472,32 +1476,84 @@ def test_no_recursion_index_on_recursion_error():
assert "maximum recursion" in str(excinfo.getrepr())
def test_exceptiongroup(pytester: Pytester) -> None:
pytester.makepyfile(
"""
def f(): raise ValueError("From f()")
def g(): raise RuntimeError("From g()")
def _exceptiongroup_common(
pytester: Pytester,
outer_chain: str,
inner_chain: str,
native: bool,
) -> None:
pre = "exceptiongroup." if not native else ""
pre2 = pre if sys.version_info < (3, 11) else ""
filestr = f"""
{"import exceptiongroup" if not native else ""}
import pytest
def main():
excs = []
for callback in [f, g]:
try:
callback()
except Exception as err:
excs.append(err)
if excs:
raise ExceptionGroup("Oops", excs)
def f(): raise ValueError("From f()")
def g(): raise BaseException("From g()")
def test():
main()
def inner(inner_chain):
excs = []
for callback in [f, g]:
try:
callback()
except BaseException as err:
excs.append(err)
if excs:
if inner_chain == "none":
raise {pre}BaseExceptionGroup("Oops", excs)
try:
raise SyntaxError()
except SyntaxError as e:
if inner_chain == "from":
raise {pre}BaseExceptionGroup("Oops", excs) from e
else:
raise {pre}BaseExceptionGroup("Oops", excs)
def outer(outer_chain, inner_chain):
try:
inner(inner_chain)
except {pre2}BaseExceptionGroup as e:
if outer_chain == "none":
raise
if outer_chain == "from":
raise IndexError() from e
else:
raise IndexError()
def test():
outer("{outer_chain}", "{inner_chain}")
"""
)
pytester.makepyfile(test_excgroup=filestr)
result = pytester.runpytest()
assert result.ret != 0
match = [
r" | ExceptionGroup: Oops (2 sub-exceptions)",
r" | ValueError: From f()",
r" | RuntimeError: From g()",
match_lines = [
rf" \| {pre2}BaseExceptionGroup: Oops \(2 sub-exceptions\)",
r" \| ValueError: From f\(\)",
r" \| BaseException: From g\(\)",
r"=* short test summary info =*",
]
result.stdout.re_match_lines(match)
if outer_chain in ("another", "from"):
match_lines.append(r"FAILED test_excgroup.py::test - IndexError")
else:
match_lines.append(
rf"FAILED test_excgroup.py::test - {pre2}BaseExceptionGroup: Oops \(2 su.*"
)
result.stdout.re_match_lines(match_lines)
@pytest.mark.skipif(
sys.version_info < (3, 11), reason="Native ExceptionGroup not implemented"
)
@pytest.mark.parametrize("outer_chain", ["none", "from", "another"])
@pytest.mark.parametrize("inner_chain", ["none", "from", "another"])
def test_native_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None:
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=True)
@pytest.mark.skipif(
"exceptiongroup" not in sys.modules, reason="exceptiongroup not installed"
)
@pytest.mark.parametrize("outer_chain", ["none", "from", "another"])
@pytest.mark.parametrize("inner_chain", ["none", "from", "another"])
def test_exceptiongroup(pytester: Pytester, outer_chain, inner_chain) -> None:
_exceptiongroup_common(pytester, outer_chain, inner_chain, native=False)

View File

@ -9,8 +9,9 @@ envlist =
py39
py310
py311
py311-exceptiongroup
pypy3
py37-{pexpect,xdist,unittestextras,numpy,pluggymain}
py37-{pexpect,xdist,unittestextras,numpy,pluggymain,exceptiongroup}
doctesting
plugins
py37-freeze
@ -46,7 +47,7 @@ setenv =
extras = testing
deps =
doctesting: PyYAML
exceptiongroup: exceptiongroup>=1.0.0
exceptiongroup: exceptiongroup>=1.0.0rc8
numpy: numpy>=1.19.4
pexpect: pexpect>=4.8.0
pluggymain: pluggy @ git+https://github.com/pytest-dev/pluggy.git