Coverage for /opt/homebrew/lib/python3.11/site-packages/_pytest/warnings.py: 74%
74 statements
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
« prev ^ index » next coverage.py v7.2.3, created at 2023-05-04 13:14 +0700
1import sys
2import warnings
3from contextlib import contextmanager
4from typing import Generator
5from typing import Optional
6from typing import TYPE_CHECKING
8import pytest
9from _pytest.config import apply_warning_filters
10from _pytest.config import Config
11from _pytest.config import parse_warning_filter
12from _pytest.main import Session
13from _pytest.nodes import Item
14from _pytest.terminal import TerminalReporter
16if TYPE_CHECKING:
17 from typing_extensions import Literal
20def pytest_configure(config: Config) -> None:
21 config.addinivalue_line(
22 "markers",
23 "filterwarnings(warning): add a warning filter to the given test. "
24 "see https://docs.pytest.org/en/stable/how-to/capture-warnings.html#pytest-mark-filterwarnings ",
25 )
28@contextmanager
29def catch_warnings_for_item(
30 config: Config,
31 ihook,
32 when: "Literal['config', 'collect', 'runtest']",
33 item: Optional[Item],
34) -> Generator[None, None, None]:
35 """Context manager that catches warnings generated in the contained execution block.
37 ``item`` can be None if we are not in the context of an item execution.
39 Each warning captured triggers the ``pytest_warning_recorded`` hook.
40 """
41 config_filters = config.getini("filterwarnings")
42 cmdline_filters = config.known_args_namespace.pythonwarnings or []
43 with warnings.catch_warnings(record=True) as log:
44 # mypy can't infer that record=True means log is not None; help it.
45 assert log is not None
47 if not sys.warnoptions:
48 # If user is not explicitly configuring warning filters, show deprecation warnings by default (#2908).
49 warnings.filterwarnings("always", category=DeprecationWarning)
50 warnings.filterwarnings("always", category=PendingDeprecationWarning)
52 apply_warning_filters(config_filters, cmdline_filters)
54 # apply filters from "filterwarnings" marks
55 nodeid = "" if item is None else item.nodeid
56 if item is not None:
57 for mark in item.iter_markers(name="filterwarnings"):
58 for arg in mark.args:
59 warnings.filterwarnings(*parse_warning_filter(arg, escape=False))
61 yield
63 for warning_message in log:
64 ihook.pytest_warning_recorded.call_historic(
65 kwargs=dict(
66 warning_message=warning_message,
67 nodeid=nodeid,
68 when=when,
69 location=None,
70 )
71 )
74def warning_record_to_str(warning_message: warnings.WarningMessage) -> str:
75 """Convert a warnings.WarningMessage to a string."""
76 warn_msg = warning_message.message
77 msg = warnings.formatwarning(
78 str(warn_msg),
79 warning_message.category,
80 warning_message.filename,
81 warning_message.lineno,
82 warning_message.line,
83 )
84 if warning_message.source is not None:
85 try:
86 import tracemalloc
87 except ImportError:
88 pass
89 else:
90 tb = tracemalloc.get_object_traceback(warning_message.source)
91 if tb is not None:
92 formatted_tb = "\n".join(tb.format())
93 # Use a leading new line to better separate the (large) output
94 # from the traceback to the previous warning text.
95 msg += f"\nObject allocated at:\n{formatted_tb}"
96 else:
97 # No need for a leading new line.
98 url = "https://docs.pytest.org/en/stable/how-to/capture-warnings.html#resource-warnings"
99 msg += "Enable tracemalloc to get traceback where the object was allocated.\n"
100 msg += f"See {url} for more info."
101 return msg
104@pytest.hookimpl(hookwrapper=True, tryfirst=True)
105def pytest_runtest_protocol(item: Item) -> Generator[None, None, None]:
106 with catch_warnings_for_item(
107 config=item.config, ihook=item.ihook, when="runtest", item=item
108 ):
109 yield
112@pytest.hookimpl(hookwrapper=True, tryfirst=True)
113def pytest_collection(session: Session) -> Generator[None, None, None]:
114 config = session.config
115 with catch_warnings_for_item(
116 config=config, ihook=config.hook, when="collect", item=None
117 ):
118 yield
121@pytest.hookimpl(hookwrapper=True)
122def pytest_terminal_summary(
123 terminalreporter: TerminalReporter,
124) -> Generator[None, None, None]:
125 config = terminalreporter.config
126 with catch_warnings_for_item(
127 config=config, ihook=config.hook, when="config", item=None
128 ):
129 yield
132@pytest.hookimpl(hookwrapper=True)
133def pytest_sessionfinish(session: Session) -> Generator[None, None, None]:
134 config = session.config
135 with catch_warnings_for_item(
136 config=config, ihook=config.hook, when="config", item=None
137 ):
138 yield
141@pytest.hookimpl(hookwrapper=True)
142def pytest_load_initial_conftests(
143 early_config: "Config",
144) -> Generator[None, None, None]:
145 with catch_warnings_for_item(
146 config=early_config, ihook=early_config.hook, when="config", item=None
147 ):
148 yield