diff --git a/src/_pytest/hookspec.py b/src/_pytest/hookspec.py index bcc38f472..18a9fb39a 100644 --- a/src/_pytest/hookspec.py +++ b/src/_pytest/hookspec.py @@ -17,6 +17,7 @@ from _pytest.compat import TYPE_CHECKING if TYPE_CHECKING: import pdb import warnings + from typing_extensions import Literal from _pytest.config import Config from _pytest.config import ExitCode @@ -675,8 +676,8 @@ def pytest_terminal_summary( @hookspec(historic=True, warn_on_impl=WARNING_CAPTURED_HOOK) def pytest_warning_captured( warning_message: "warnings.WarningMessage", - when: str, - item, + when: "Literal['config', 'collect', 'runtest']", + item: "Optional[Item]", location: Optional[Tuple[str, int, str]], ) -> None: """(**Deprecated**) Process a warning captured by the internal pytest warnings plugin. @@ -710,7 +711,7 @@ def pytest_warning_captured( @hookspec(historic=True) def pytest_warning_recorded( warning_message: "warnings.WarningMessage", - when: str, + when: "Literal['config', 'collect', 'runtest']", nodeid: str, location: Optional[Tuple[str, int, str]], ) -> None: diff --git a/src/_pytest/warnings.py b/src/_pytest/warnings.py index 622cbb806..5cedba244 100644 --- a/src/_pytest/warnings.py +++ b/src/_pytest/warnings.py @@ -4,6 +4,7 @@ import warnings from contextlib import contextmanager from functools import lru_cache from typing import Generator +from typing import Optional from typing import Tuple import pytest @@ -15,7 +16,8 @@ from _pytest.nodes import Item from _pytest.terminal import TerminalReporter if TYPE_CHECKING: - from typing_extensions import Type + from typing import Type + from typing_extensions import Literal @lru_cache(maxsize=50) @@ -79,7 +81,12 @@ def pytest_configure(config: Config) -> None: @contextmanager -def catch_warnings_for_item(config, ihook, when, item): +def catch_warnings_for_item( + config: Config, + ihook, + when: "Literal['config', 'collect', 'runtest']", + item: Optional[Item], +) -> Generator[None, None, None]: """ Context manager that catches warnings generated in the contained execution block. @@ -133,11 +140,11 @@ def catch_warnings_for_item(config, ihook, when, item): ) -def warning_record_to_str(warning_message): +def warning_record_to_str(warning_message: warnings.WarningMessage) -> str: """Convert a warnings.WarningMessage to a string.""" warn_msg = warning_message.message msg = warnings.formatwarning( - warn_msg, + str(warn_msg), warning_message.category, warning_message.filename, warning_message.lineno, @@ -175,7 +182,7 @@ def pytest_terminal_summary( @pytest.hookimpl(hookwrapper=True) -def pytest_sessionfinish(session): +def pytest_sessionfinish(session: Session) -> Generator[None, None, None]: config = session.config with catch_warnings_for_item( config=config, ihook=config.hook, when="config", item=None @@ -183,7 +190,7 @@ def pytest_sessionfinish(session): yield -def _issue_warning_captured(warning, hook, stacklevel): +def _issue_warning_captured(warning: Warning, hook, stacklevel: int) -> None: """ This function should be used instead of calling ``warnings.warn`` directly when we are in the "configure" stage: at this point the actual options might not have been set, so we manually trigger the pytest_warning_recorded @@ -196,8 +203,6 @@ def _issue_warning_captured(warning, hook, stacklevel): with warnings.catch_warnings(record=True) as records: warnings.simplefilter("always", type(warning)) warnings.warn(warning, stacklevel=stacklevel) - # Mypy can't infer that record=True means records is not None; help it. - assert records is not None frame = sys._getframe(stacklevel - 1) location = frame.f_code.co_filename, frame.f_lineno, frame.f_code.co_name hook.pytest_warning_captured.call_historic(