Skip to content
Merged
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: "v0.5.2"
rev: "v0.6.2"
hooks:
- id: ruff
args: ["--fix"]
Expand All @@ -21,7 +21,7 @@ repos:
hooks:
- id: python-use-type-annotations
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
rev: v1.11.2
hooks:
- id: mypy
files: ^(src/|testing/|scripts/)
Expand All @@ -32,19 +32,19 @@ repos:
- pluggy>=1.5.0
- packaging
- tomli
- types-pkg_resources
- types-setuptools
- types-tabulate
# for mypy running on python>=3.11 since exceptiongroup is only a dependency
# on <3.11
- exceptiongroup>=1.0.0rc8
- repo: https://github.com/tox-dev/pyproject-fmt
rev: "2.1.4"
rev: "2.2.1"
hooks:
- id: pyproject-fmt
# https://pyproject-fmt.readthedocs.io/en/latest/#calculating-max-supported-python-version
additional_dependencies: ["tox>=4.9"]
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
rev: v3.17.0
hooks:
- id: pyupgrade
stages: [manual]
Expand Down
1 change: 1 addition & 0 deletions changelog/12744.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed typing compatibility with Python 3.9 or less -- replaced `typing.Self` with `typing_extensions.Self` -- by :user:`Avasam`
2 changes: 1 addition & 1 deletion doc/en/broken-dep-constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

# Pin towncrier temporarily due to incompatibility with sphinxcontrib-towncrier:
# https://github.com/sphinx-contrib/sphinxcontrib-towncrier/issues/92
towncrier!=24.7.0,!=24.7.1
towncrier<24.7
4 changes: 2 additions & 2 deletions src/_pytest/_io/pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,15 @@
p(self, object, stream, indent, allowance, context, level + 1)
context.remove(objid)
elif (
_dataclasses.is_dataclass(object)
_dataclasses.is_dataclass(object) # type:ignore[unreachable]
and not isinstance(object, type)
and object.__dataclass_params__.repr
and
# Check dataclass has generated repr method.
hasattr(object.__repr__, "__wrapped__")
and "__create_fn__" in object.__repr__.__wrapped__.__qualname__
):
context.add(objid)
context.add(objid) # type:ignore[unreachable]

Check warning on line 122 in src/_pytest/_io/pprint.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/_io/pprint.py#L122

Added line #L122 was not covered by tests
self._pprint_dataclass(
object, stream, indent, allowance, context, level + 1
)
Expand Down
6 changes: 2 additions & 4 deletions src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,7 @@ def pytest_collectreport(self, report: CollectReport) -> None:
@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, config: Config, items: list[nodes.Item]
) -> Generator[None, None, None]:
) -> Generator[None]:
res = yield

if not self.active:
Expand Down Expand Up @@ -439,9 +439,7 @@ def __init__(self, config: Config) -> None:
self.cached_nodeids = set(config.cache.get("cache/nodeids", []))

@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection_modifyitems(
self, items: list[nodes.Item]
) -> Generator[None, None, None]:
def pytest_collection_modifyitems(self, items: list[nodes.Item]) -> Generator[None]:
res = yield

if self.active:
Expand Down
23 changes: 12 additions & 11 deletions src/_pytest/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@


@hookimpl(wrapper=True)
def pytest_load_initial_conftests(early_config: Config) -> Generator[None, None, None]:
def pytest_load_initial_conftests(early_config: Config) -> Generator[None]:
ns = early_config.known_args_namespace
if ns.capture == "fd":
_windowsconsoleio_workaround(sys.stdout)
Expand Down Expand Up @@ -202,6 +202,7 @@
class DontReadFromInput(TextIO):
@property
def encoding(self) -> str:
assert sys.__stdin__ is not None

Check warning on line 205 in src/_pytest/capture.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/capture.py#L205

Added line #L205 was not covered by tests
return sys.__stdin__.encoding

def read(self, size: int = -1) -> str:
Expand Down Expand Up @@ -817,7 +818,7 @@
# Helper context managers

@contextlib.contextmanager
def global_and_fixture_disabled(self) -> Generator[None, None, None]:
def global_and_fixture_disabled(self) -> Generator[None]:
"""Context manager to temporarily disable global and current fixture capturing."""
do_fixture = self._capture_fixture and self._capture_fixture._is_started()
if do_fixture:
Expand All @@ -834,7 +835,7 @@
self.resume_fixture()

@contextlib.contextmanager
def item_capture(self, when: str, item: Item) -> Generator[None, None, None]:
def item_capture(self, when: str, item: Item) -> Generator[None]:
self.resume_global_capture()
self.activate_fixture()
try:
Expand Down Expand Up @@ -869,17 +870,17 @@
return rep

@hookimpl(wrapper=True)
def pytest_runtest_setup(self, item: Item) -> Generator[None, None, None]:
def pytest_runtest_setup(self, item: Item) -> Generator[None]:
with self.item_capture("setup", item):
return (yield)

@hookimpl(wrapper=True)
def pytest_runtest_call(self, item: Item) -> Generator[None, None, None]:
def pytest_runtest_call(self, item: Item) -> Generator[None]:
with self.item_capture("call", item):
return (yield)

@hookimpl(wrapper=True)
def pytest_runtest_teardown(self, item: Item) -> Generator[None, None, None]:
def pytest_runtest_teardown(self, item: Item) -> Generator[None]:
with self.item_capture("teardown", item):
return (yield)

Expand Down Expand Up @@ -961,7 +962,7 @@
return False

@contextlib.contextmanager
def disabled(self) -> Generator[None, None, None]:
def disabled(self) -> Generator[None]:
"""Temporarily disable capturing while inside the ``with`` block."""
capmanager: CaptureManager = self.request.config.pluginmanager.getplugin(
"capturemanager"
Expand All @@ -974,7 +975,7 @@


@fixture
def capsys(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
def capsys(request: SubRequest) -> Generator[CaptureFixture[str]]:
r"""Enable text capturing of writes to ``sys.stdout`` and ``sys.stderr``.

The captured output is made available via ``capsys.readouterr()`` method
Expand Down Expand Up @@ -1002,7 +1003,7 @@


@fixture
def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
def capsysbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]:
r"""Enable bytes capturing of writes to ``sys.stdout`` and ``sys.stderr``.

The captured output is made available via ``capsysbinary.readouterr()``
Expand Down Expand Up @@ -1030,7 +1031,7 @@


@fixture
def capfd(request: SubRequest) -> Generator[CaptureFixture[str], None, None]:
def capfd(request: SubRequest) -> Generator[CaptureFixture[str]]:
r"""Enable text capturing of writes to file descriptors ``1`` and ``2``.

The captured output is made available via ``capfd.readouterr()`` method
Expand Down Expand Up @@ -1058,7 +1059,7 @@


@fixture
def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes], None, None]:
def capfdbinary(request: SubRequest) -> Generator[CaptureFixture[bytes]]:
r"""Enable bytes capturing of writes to file descriptors ``1`` and ``2``.

The captured output is made available via ``capfd.readouterr()`` method
Expand Down
5 changes: 3 additions & 2 deletions src/_pytest/doctest.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@

if TYPE_CHECKING:
import doctest
from typing import Self

from typing_extensions import Self

DOCTEST_REPORT_CHOICE_NONE = "none"
DOCTEST_REPORT_CHOICE_CDIFF = "cdiff"
Expand Down Expand Up @@ -467,7 +468,7 @@ def _is_mocked(obj: object) -> bool:


@contextmanager
def _patch_unwrap_mock_aware() -> Generator[None, None, None]:
def _patch_unwrap_mock_aware() -> Generator[None]:
"""Context manager which replaces ``inspect.unwrap`` with a version
that's aware of mock objects and doesn't recurse into them."""
real_unwrap = inspect.unwrap
Expand Down
1 change: 1 addition & 0 deletions src/_pytest/faulthandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def get_stderr_fileno() -> int:
# pytest-xdist monkeypatches sys.stderr with an object that is not an actual file.
# https://docs.python.org/3/library/faulthandler.html#issue-with-file-descriptors
# This is potentially dangerous, but the best we can do.
assert sys.__stderr__ is not None
return sys.__stderr__.fileno()


Expand Down
22 changes: 10 additions & 12 deletions src/_pytest/logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -554,9 +554,7 @@ def set_level(self, level: int | str, logger: str | None = None) -> None:
self._initial_disabled_logging_level = initial_disabled_logging_level

@contextmanager
def at_level(
self, level: int | str, logger: str | None = None
) -> Generator[None, None, None]:
def at_level(self, level: int | str, logger: str | None = None) -> Generator[None]:
"""Context manager that sets the level for capturing of logs. After
the end of the 'with' statement the level is restored to its original
value.
Expand All @@ -580,7 +578,7 @@ def at_level(
logging.disable(original_disable_level)

@contextmanager
def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]:
def filtering(self, filter_: logging.Filter) -> Generator[None]:
"""Context manager that temporarily adds the given filter to the caplog's
:meth:`handler` for the 'with' statement block, and removes that filter at the
end of the block.
Expand All @@ -597,7 +595,7 @@ def filtering(self, filter_: logging.Filter) -> Generator[None, None, None]:


@fixture
def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture, None, None]:
def caplog(request: FixtureRequest) -> Generator[LogCaptureFixture]:
"""Access and control log capturing.

Captured logs are available through the following properties/methods::
Expand Down Expand Up @@ -776,15 +774,15 @@ def _log_cli_enabled(self) -> bool:
return True

@hookimpl(wrapper=True, tryfirst=True)
def pytest_sessionstart(self) -> Generator[None, None, None]:
def pytest_sessionstart(self) -> Generator[None]:
self.log_cli_handler.set_when("sessionstart")

with catching_logs(self.log_cli_handler, level=self.log_cli_level):
with catching_logs(self.log_file_handler, level=self.log_file_level):
return (yield)

@hookimpl(wrapper=True, tryfirst=True)
def pytest_collection(self) -> Generator[None, None, None]:
def pytest_collection(self) -> Generator[None]:
self.log_cli_handler.set_when("collection")

with catching_logs(self.log_cli_handler, level=self.log_cli_level):
Expand Down Expand Up @@ -813,7 +811,7 @@ def pytest_runtest_logstart(self) -> None:
def pytest_runtest_logreport(self) -> None:
self.log_cli_handler.set_when("logreport")

def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, None]:
def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None]:
"""Implement the internals of the pytest_runtest_xxx() hooks."""
with catching_logs(
self.caplog_handler,
Expand All @@ -834,21 +832,21 @@ def _runtest_for(self, item: nodes.Item, when: str) -> Generator[None, None, Non
item.add_report_section(when, "log", log)

@hookimpl(wrapper=True)
def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None, None, None]:
def pytest_runtest_setup(self, item: nodes.Item) -> Generator[None]:
self.log_cli_handler.set_when("setup")

empty: dict[str, list[logging.LogRecord]] = {}
item.stash[caplog_records_key] = empty
yield from self._runtest_for(item, "setup")

@hookimpl(wrapper=True)
def pytest_runtest_call(self, item: nodes.Item) -> Generator[None, None, None]:
def pytest_runtest_call(self, item: nodes.Item) -> Generator[None]:
self.log_cli_handler.set_when("call")

yield from self._runtest_for(item, "call")

@hookimpl(wrapper=True)
def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None, None, None]:
def pytest_runtest_teardown(self, item: nodes.Item) -> Generator[None]:
self.log_cli_handler.set_when("teardown")

try:
Expand All @@ -862,7 +860,7 @@ def pytest_runtest_logfinish(self) -> None:
self.log_cli_handler.set_when("finish")

@hookimpl(wrapper=True, tryfirst=True)
def pytest_sessionfinish(self) -> Generator[None, None, None]:
def pytest_sessionfinish(self) -> Generator[None]:
self.log_cli_handler.set_when("sessionfinish")

with catching_logs(self.log_cli_handler, level=self.log_cli_level):
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/monkeypatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@


@fixture
def monkeypatch() -> Generator[MonkeyPatch, None, None]:
def monkeypatch() -> Generator[MonkeyPatch]:
"""A convenient fixture for monkey-patching.

The fixture provides these methods to modify objects, dictionaries, or
Expand Down Expand Up @@ -135,7 +135,7 @@ def __init__(self) -> None:

@classmethod
@contextmanager
def context(cls) -> Generator[MonkeyPatch, None, None]:
def context(cls) -> Generator[MonkeyPatch]:
"""Context manager that returns a new :class:`MonkeyPatch` object
which undoes any patching done inside the ``with`` block upon exit.

Expand Down
2 changes: 1 addition & 1 deletion src/_pytest/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@


if TYPE_CHECKING:
from typing import Self
from typing_extensions import Self

# Imported here due to circular import.
from _pytest.main import Session
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/pytester.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def pytester(


@fixture
def _sys_snapshot() -> Generator[None, None, None]:
def _sys_snapshot() -> Generator[None]:
snappaths = SysPathsSnapshot()
snapmods = SysModulesSnapshot()
yield
Expand All @@ -500,7 +500,7 @@ def _sys_snapshot() -> Generator[None, None, None]:


@fixture
def _config_for_test() -> Generator[Config, None, None]:
def _config_for_test() -> Generator[Config]:
from _pytest.config import get_config

config = get_config()
Expand Down
10 changes: 5 additions & 5 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@


if TYPE_CHECKING:
from typing import Self
from typing_extensions import Self


def pytest_addoption(parser: Parser) -> None:
Expand Down Expand Up @@ -568,7 +568,7 @@
if setup_module is None and teardown_module is None:
return

def xunit_setup_module_fixture(request) -> Generator[None, None, None]:
def xunit_setup_module_fixture(request) -> Generator[None]:
module = request.module
if setup_module is not None:
_call_with_optional_argument(setup_module, module)
Expand Down Expand Up @@ -599,7 +599,7 @@
if setup_function is None and teardown_function is None:
return

def xunit_setup_function_fixture(request) -> Generator[None, None, None]:
def xunit_setup_function_fixture(request) -> Generator[None]:

Check warning on line 602 in src/_pytest/python.py

View check run for this annotation

Codecov / codecov/patch

src/_pytest/python.py#L602

Added line #L602 was not covered by tests
if request.instance is not None:
# in this case we are bound to an instance, so we need to let
# setup_method handle this
Expand Down Expand Up @@ -780,7 +780,7 @@
if setup_class is None and teardown_class is None:
return

def xunit_setup_class_fixture(request) -> Generator[None, None, None]:
def xunit_setup_class_fixture(request) -> Generator[None]:
cls = request.cls
if setup_class is not None:
func = getimfunc(setup_class)
Expand Down Expand Up @@ -813,7 +813,7 @@
if setup_method is None and teardown_method is None:
return

def xunit_setup_method_fixture(request) -> Generator[None, None, None]:
def xunit_setup_method_fixture(request) -> Generator[None]:
instance = request.instance
method = request.function
if setup_method is not None:
Expand Down
Loading
Loading