|
| 1 | +from __future__ import annotations |
| 2 | + |
1 | 3 | import importlib
|
2 | 4 | import io
|
3 | 5 | import operator
|
4 | 6 | import queue
|
| 7 | +import re |
5 | 8 | import sys
|
6 | 9 | import textwrap
|
7 | 10 | from pathlib import Path
|
8 | 11 | from typing import Any
|
9 |
| -from typing import Dict |
10 |
| -from typing import Tuple |
11 | 12 | from typing import TYPE_CHECKING
|
12 |
| -from typing import Union |
13 | 13 |
|
14 | 14 | import _pytest._code
|
15 | 15 | import pytest
|
@@ -801,7 +801,7 @@ def entry():
|
801 | 801 | )
|
802 | 802 | excinfo = pytest.raises(ValueError, mod.entry)
|
803 | 803 |
|
804 |
| - styles: Tuple[_TracebackStyle, ...] = ("long", "short") |
| 804 | + styles: tuple[_TracebackStyle, ...] = ("long", "short") |
805 | 805 | for style in styles:
|
806 | 806 | p = FormattedExcinfo(style=style)
|
807 | 807 | reprtb = p.repr_traceback(excinfo)
|
@@ -928,7 +928,7 @@ def entry():
|
928 | 928 | )
|
929 | 929 | excinfo = pytest.raises(ValueError, mod.entry)
|
930 | 930 |
|
931 |
| - styles: Tuple[_TracebackStyle, ...] = ("short", "long", "no") |
| 931 | + styles: tuple[_TracebackStyle, ...] = ("short", "long", "no") |
932 | 932 | for style in styles:
|
933 | 933 | for showlocals in (True, False):
|
934 | 934 | repr = excinfo.getrepr(style=style, showlocals=showlocals)
|
@@ -1090,7 +1090,7 @@ def f():
|
1090 | 1090 | for funcargs in (True, False)
|
1091 | 1091 | ],
|
1092 | 1092 | )
|
1093 |
| - def test_format_excinfo(self, reproptions: Dict[str, Any]) -> None: |
| 1093 | + def test_format_excinfo(self, reproptions: dict[str, Any]) -> None: |
1094 | 1094 | def bar():
|
1095 | 1095 | assert False, "some error"
|
1096 | 1096 |
|
@@ -1398,7 +1398,7 @@ def f():
|
1398 | 1398 | @pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
1399 | 1399 | def test_repr_traceback_with_unicode(style, encoding):
|
1400 | 1400 | if encoding is None:
|
1401 |
| - msg: Union[str, bytes] = "☹" |
| 1401 | + msg: str | bytes = "☹" |
1402 | 1402 | else:
|
1403 | 1403 | msg = "☹".encode(encoding)
|
1404 | 1404 | try:
|
@@ -1648,3 +1648,51 @@ def test():
|
1648 | 1648 | ],
|
1649 | 1649 | consecutive=True,
|
1650 | 1650 | )
|
| 1651 | + |
| 1652 | + |
| 1653 | +def add_note(err: BaseException, msg: str) -> None: |
| 1654 | + """Adds a note to an exception inplace.""" |
| 1655 | + if sys.version_info < (3, 11): |
| 1656 | + err.__notes__ = getattr(err, "__notes__", []) + [msg] # type: ignore[attr-defined] |
| 1657 | + else: |
| 1658 | + err.add_note(msg) |
| 1659 | + |
| 1660 | + |
| 1661 | +@pytest.mark.parametrize( |
| 1662 | + "error,notes,match", |
| 1663 | + [ |
| 1664 | + (Exception("test"), [], "test"), |
| 1665 | + (AssertionError("foo"), ["bar"], "bar"), |
| 1666 | + (AssertionError("foo"), ["bar", "baz"], "bar"), |
| 1667 | + (AssertionError("foo"), ["bar", "baz"], "baz"), |
| 1668 | + (ValueError("foo"), ["bar", "baz"], re.compile(r"bar\nbaz", re.MULTILINE)), |
| 1669 | + (ValueError("foo"), ["bar", "baz"], re.compile(r"BAZ", re.IGNORECASE)), |
| 1670 | + ], |
| 1671 | +) |
| 1672 | +def test_check_error_notes_success( |
| 1673 | + error: Exception, notes: list[str], match: str |
| 1674 | +) -> None: |
| 1675 | + for note in notes: |
| 1676 | + add_note(error, note) |
| 1677 | + |
| 1678 | + with pytest.raises(Exception, match=match): |
| 1679 | + raise error |
| 1680 | + |
| 1681 | + |
| 1682 | +@pytest.mark.parametrize( |
| 1683 | + "error, notes, match", |
| 1684 | + [ |
| 1685 | + (Exception("test"), [], "foo"), |
| 1686 | + (AssertionError("foo"), ["bar"], "baz"), |
| 1687 | + (AssertionError("foo"), ["bar"], "foo\nbaz"), |
| 1688 | + ], |
| 1689 | +) |
| 1690 | +def test_check_error_notes_failure( |
| 1691 | + error: Exception, notes: list[str], match: str |
| 1692 | +) -> None: |
| 1693 | + for note in notes: |
| 1694 | + add_note(error, note) |
| 1695 | + |
| 1696 | + with pytest.raises(AssertionError): |
| 1697 | + with pytest.raises(type(error), match=match): |
| 1698 | + raise error |
0 commit comments