Skip to content

Commit

Permalink
Derive pytest.raises from AbstractContextManager
Browse files Browse the repository at this point in the history
Makes `AbstractContextManager` the shared base class between "raises" and other context managers.

The motivation is for type checkers to narrow `pytest.raises(...) if x else nullcontext()` to a `ContextManager` rather than `object`.
  • Loading branch information
ikonst committed Jan 13, 2023
1 parent 3ad4344 commit f5c5a85
Show file tree
Hide file tree
Showing 2 changed files with 12 additions and 2 deletions.
4 changes: 2 additions & 2 deletions src/_pytest/python_api.py
Expand Up @@ -8,7 +8,7 @@
from typing import Any
from typing import Callable
from typing import cast
from typing import Generic
from typing import ContextManager
from typing import List
from typing import Mapping
from typing import Optional
Expand Down Expand Up @@ -957,7 +957,7 @@ def raises( # noqa: F811


@final
class RaisesContext(Generic[E]):
class RaisesContext(ContextManager[_pytest._code.ExceptionInfo[E]]):
def __init__(
self,
expected_exception: Union[Type[E], Tuple[Type[E], ...]],
Expand Down
10 changes: 10 additions & 0 deletions testing/typing_checks.py
Expand Up @@ -3,7 +3,11 @@
This file is not executed, it is only checked by mypy to ensure that
none of the code triggers any mypy errors.
"""
import contextlib
from typing import Optional

import pytest
from typing_extensions import assert_type


# Issue #7488.
Expand All @@ -22,3 +26,9 @@ def check_fixture_ids_callable() -> None:
@pytest.mark.parametrize("func", [str, int], ids=lambda x: str(x.__name__))
def check_parametrize_ids_callable(func) -> None:
pass


def check_raises_is_a_context_manager(val: bool) -> None:
with pytest.raises(RuntimeError) if val else contextlib.nullcontext() as excinfo:
pass
assert_type(excinfo, Optional[pytest.ExceptionInfo[RuntimeError]])

0 comments on commit f5c5a85

Please sign in to comment.