From aaeb1ca107ebcded73f05a2130ae0db7f65abf94 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 20:46:58 +0000 Subject: [PATCH 1/4] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.12.12 → v0.13.0](https://github.com/astral-sh/ruff-pre-commit/compare/v0.12.12...v0.13.0) - [github.com/woodruffw/zizmor-pre-commit: v1.12.1 → v1.13.0](https://github.com/woodruffw/zizmor-pre-commit/compare/v1.12.1...v1.13.0) - [github.com/pre-commit/mirrors-mypy: v1.17.1 → v1.18.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.17.1...v1.18.1) --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4b7ea12fa4c..c6cb8da92cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.12.12" + rev: "v0.13.0" hooks: - id: ruff args: ["--fix"] @@ -12,7 +12,7 @@ repos: - id: end-of-file-fixer - id: check-yaml - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.12.1 + rev: v1.13.0 hooks: - id: zizmor - repo: https://github.com/adamchainz/blacken-docs @@ -32,7 +32,7 @@ repos: hooks: - id: python-use-type-annotations - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.17.1 + rev: v1.18.1 hooks: - id: mypy files: ^(src/|testing/|scripts/) From 779cecde7caedd6cc59f2ee1c4f689a45246f3c5 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 16 Sep 2025 09:48:10 -0300 Subject: [PATCH 2/4] Comply with new lint rule for unused variables --- doc/en/example/.ruff.toml | 1 + src/_pytest/_code/source.py | 2 +- src/_pytest/_py/path.py | 2 +- src/_pytest/config/__init__.py | 6 ++-- src/_pytest/helpconfig.py | 2 +- src/_pytest/mark/structures.py | 2 +- src/_pytest/reports.py | 2 +- testing/_py/test_local.py | 3 +- testing/acceptance_test.py | 5 ++- testing/code/test_code.py | 4 +-- testing/code/test_excinfo.py | 2 +- testing/code/test_source.py | 4 +-- .../sub2/conftest.py | 4 ++- testing/python/collect.py | 6 ++-- testing/python/fixtures.py | 2 +- testing/python/integration.py | 4 +-- testing/python/metafunc.py | 2 +- testing/test_capture.py | 16 +++++----- testing/test_collection.py | 20 ++++++------ testing/test_doctest.py | 14 ++++---- testing/test_junitxml.py | 32 +++++++++---------- testing/test_legacypath.py | 4 +-- testing/test_main.py | 4 +-- testing/test_mark.py | 28 ++++++++-------- testing/test_nodes.py | 8 ++--- testing/test_pathlib.py | 2 +- testing/test_pluginmanager.py | 2 +- testing/test_pytester.py | 2 +- testing/test_reports.py | 8 ++--- testing/test_session.py | 2 +- testing/test_skipping.py | 6 ++-- testing/test_tmpdir.py | 2 +- testing/test_unittest.py | 14 ++++---- testing/test_warning_types.py | 2 +- 34 files changed, 111 insertions(+), 108 deletions(-) create mode 100644 doc/en/example/.ruff.toml diff --git a/doc/en/example/.ruff.toml b/doc/en/example/.ruff.toml new file mode 100644 index 00000000000..1cbca296f8c --- /dev/null +++ b/doc/en/example/.ruff.toml @@ -0,0 +1 @@ +ignore = ["RUF059"] diff --git a/src/_pytest/_code/source.py b/src/_pytest/_code/source.py index 280691f0b6c..99c242dd98e 100644 --- a/src/_pytest/_code/source.py +++ b/src/_pytest/_code/source.py @@ -103,7 +103,7 @@ def getstatementrange(self, lineno: int) -> tuple[int, int]: which containing the given lineno.""" if not (0 <= lineno < len(self)): raise IndexError("lineno out of range") - ast, start, end = getstatementrange_ast(lineno, self) + _ast, start, end = getstatementrange_ast(lineno, self) return start, end def deindent(self) -> Source: diff --git a/src/_pytest/_py/path.py b/src/_pytest/_py/path.py index 878fc7a538b..b7131b08a20 100644 --- a/src/_pytest/_py/path.py +++ b/src/_pytest/_py/path.py @@ -652,7 +652,7 @@ def new(self, **kw): if not kw: obj.strpath = self.strpath return obj - drive, dirname, basename, purebasename, ext = self._getbyspec( + drive, dirname, _basename, purebasename, ext = self._getbyspec( "drive,dirname,basename,purebasename,ext" ) if "basename" in kw: diff --git a/src/_pytest/config/__init__.py b/src/_pytest/config/__init__.py index 8c80405a6ab..38fb1ee6d27 100644 --- a/src/_pytest/config/__init__.py +++ b/src/_pytest/config/__init__.py @@ -1217,7 +1217,7 @@ def pytest_load_initial_conftests(self, early_config: Config) -> None: # early_config.args it not set yet. But we need it for # discovering the initial conftests. So "pre-run" the logic here. # It will be done for real in `parse()`. - args, args_source = early_config._decide_args( + args, _args_source = early_config._decide_args( args=early_config.known_args_namespace.file_or_dir, pyargs=early_config.known_args_namespace.pyargs, testpaths=early_config.getini("testpaths"), @@ -1273,7 +1273,7 @@ def _consider_importhook(self, args: Sequence[str]) -> None: and find all the installed plugins to mark them for rewriting by the importhook. """ - ns, unknown_args = self._parser.parse_known_and_unknown_args(args) + ns, _unknown_args = self._parser.parse_known_and_unknown_args(args) mode = getattr(ns, "assertmode", "plain") disable_autoload = getattr(ns, "disable_plugin_autoload", False) or bool( @@ -1630,7 +1630,7 @@ def _getini_unknown_type(self, name: str, type: str, value: object): def _getini(self, name: str): try: - description, type, default = self._parser._inidict[name] + _description, type, default = self._parser._inidict[name] except KeyError as e: raise ValueError(f"unknown configuration value: {name!r}") from e override_value = self._get_override_ini_value(name) diff --git a/src/_pytest/helpconfig.py b/src/_pytest/helpconfig.py index b5ac0e6a50c..a531c75d28b 100644 --- a/src/_pytest/helpconfig.py +++ b/src/_pytest/helpconfig.py @@ -185,7 +185,7 @@ def showhelp(config: Config) -> None: indent_len = 24 # based on argparse's max_help_position=24 indent = " " * indent_len for name in config._parser._ininames: - help, type, default = config._parser._inidict[name] + help, type, _default = config._parser._inidict[name] if type is None: type = "string" if help is None: diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 04c37796e10..d0280e26945 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -60,7 +60,7 @@ def get_empty_parameterset_mark( argslisting = ", ".join(argnames) - fs, lineno = getfslineno(func) + _fs, lineno = getfslineno(func) reason = f"got empty parameter set for ({argslisting})" requested_mark = config.getini(EMPTY_PARAMETERSET_OPTION) if requested_mark in ("", None, "skip"): diff --git a/src/_pytest/reports.py b/src/_pytest/reports.py index 2122c021020..fb0607bfb95 100644 --- a/src/_pytest/reports.py +++ b/src/_pytest/reports.py @@ -188,7 +188,7 @@ def head_line(self) -> str | None: even in patch releases. """ if self.location is not None: - fspath, lineno, domain = self.location + _fspath, _lineno, domain = self.location return domain return None diff --git a/testing/_py/test_local.py b/testing/_py/test_local.py index ab62e93b223..673ce9b3de6 100644 --- a/testing/_py/test_local.py +++ b/testing/_py/test_local.py @@ -9,9 +9,10 @@ from unittest import mock import warnings -from py import error from py.path import local +from py import error + import pytest diff --git a/testing/acceptance_test.py b/testing/acceptance_test.py index 2101fe9ad76..544b2cd3f0f 100644 --- a/testing/acceptance_test.py +++ b/testing/acceptance_test.py @@ -644,10 +644,9 @@ def test_invoke_with_invalid_type(self) -> None: ): pytest.main("-h") # type: ignore[arg-type] - def test_invoke_with_path(self, pytester: Pytester, capsys) -> None: + def test_invoke_with_path(self, pytester: Pytester) -> None: retcode = pytest.main([str(pytester.path)]) assert retcode == ExitCode.NO_TESTS_COLLECTED - out, err = capsys.readouterr() def test_invoke_plugin_api(self, capsys) -> None: class MyPlugin: @@ -655,7 +654,7 @@ def pytest_addoption(self, parser): parser.addoption("--myopt") pytest.main(["-h"], plugins=[MyPlugin()]) - out, err = capsys.readouterr() + out, _err = capsys.readouterr() assert "--myopt" in out def test_pyargs_importerror(self, pytester: Pytester, monkeypatch) -> None: diff --git a/testing/code/test_code.py b/testing/code/test_code.py index 7ae5ad46100..ae5e0e949cf 100644 --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -86,9 +86,9 @@ def test_unicode_handling() -> None: value = "ąć".encode() def f() -> None: - raise Exception(value) + raise ValueError(value) - excinfo = pytest.raises(Exception, f) + excinfo = pytest.raises(ValueError, f) str(excinfo) diff --git a/testing/code/test_excinfo.py b/testing/code/test_excinfo.py index 555645030fc..636469ee6f1 100644 --- a/testing/code/test_excinfo.py +++ b/testing/code/test_excinfo.py @@ -263,7 +263,7 @@ def do_stuff() -> None: def reraise_me() -> None: import sys - exc, val, tb = sys.exc_info() + _exc, val, tb = sys.exc_info() assert val is not None raise val.with_traceback(tb) diff --git a/testing/code/test_source.py b/testing/code/test_source.py index 321372d4b59..e413af3766e 100644 --- a/testing/code/test_source.py +++ b/testing/code/test_source.py @@ -399,7 +399,7 @@ def getstatement(lineno: int, source) -> Source: from _pytest._code.source import getstatementrange_ast src = Source(source) - ast, start, end = getstatementrange_ast(lineno, src) + _ast, start, end = getstatementrange_ast(lineno, src) return src[start:end] @@ -418,7 +418,7 @@ def test_comment_and_no_newline_at_end() -> None: "# vim: filetype=pyopencl:fdm=marker", ] ) - ast, start, end = getstatementrange_ast(1, source) + _ast, _start, end = getstatementrange_ast(1, source) assert end == 2 diff --git a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py index 112d1e05f27..0185628c3a0 100644 --- a/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py +++ b/testing/example_scripts/fixtures/fill_fixtures/test_conftest_funcargs_only_available_in_subdir/sub2/conftest.py @@ -1,9 +1,11 @@ # mypy: allow-untyped-defs from __future__ import annotations +from _pytest.fixtures import FixtureLookupError import pytest @pytest.fixture def arg2(request): - pytest.raises(Exception, request.getfixturevalue, "arg1") + with pytest.raises(FixtureLookupError): + request.getfixturevalue("arg1") diff --git a/testing/python/collect.py b/testing/python/collect.py index 80981ed280f..b26931007d9 100644 --- a/testing/python/collect.py +++ b/testing/python/collect.py @@ -1273,10 +1273,10 @@ def test_bar(self): ) classcol = pytester.collect_by_name(modcol, "TestClass") assert isinstance(classcol, Class) - path, lineno, msg = classcol.reportinfo() + _path, _lineno, _msg = classcol.reportinfo() func = next(iter(classcol.collect())) assert isinstance(func, Function) - path, lineno, msg = func.reportinfo() + _path, _lineno, _msg = func.reportinfo() def test_customized_python_discovery(pytester: Pytester) -> None: @@ -1489,7 +1489,7 @@ def test_package_collection_init_given_as_argument(pytester: Pytester) -> None: Module, not the entire package. """ p = pytester.copy_example("collect/package_init_given_as_arg") - items, hookrecorder = pytester.inline_genitems(p / "pkg" / "__init__.py") + items, _hookrecorder = pytester.inline_genitems(p / "pkg" / "__init__.py") assert len(items) == 1 assert items[0].name == "test_init" diff --git a/testing/python/fixtures.py b/testing/python/fixtures.py index b58ddc6e162..8b97d35c21e 100644 --- a/testing/python/fixtures.py +++ b/testing/python/fixtures.py @@ -1150,7 +1150,7 @@ def test_session_scoped_unavailable_attributes(self, session_request, name): class TestRequestMarking: def test_applymarker(self, pytester: Pytester) -> None: - item1, item2 = pytester.getitems( + item1, _item2 = pytester.getitems( """ import pytest diff --git a/testing/python/integration.py b/testing/python/integration.py index c96b6e4c260..d8f8d0ffae9 100644 --- a/testing/python/integration.py +++ b/testing/python/integration.py @@ -21,8 +21,8 @@ def wrap(f): def wrapped_func(x, y, z): pass - fs, lineno = getfslineno(wrapped_func) - fs2, lineno2 = getfslineno(wrap) + _fs, lineno = getfslineno(wrapped_func) + _fs2, lineno2 = getfslineno(wrap) assert lineno > lineno2, "getfslineno does not unwrap correctly" diff --git a/testing/python/metafunc.py b/testing/python/metafunc.py index 010c22f5c0c..20ccacf4b73 100644 --- a/testing/python/metafunc.py +++ b/testing/python/metafunc.py @@ -92,7 +92,7 @@ def func(x, y): with pytest.raises(pytest.Collector.CollectError): metafunc.parametrize("y", [5, 6]) - with pytest.raises(TypeError, match="^ids must be a callable or an iterable$"): + with pytest.raises(TypeError, match=r"^ids must be a callable or an iterable$"): metafunc.parametrize("y", [5, 6], ids=42) # type: ignore[arg-type] def test_parametrize_error_iterator(self) -> None: diff --git a/testing/test_capture.py b/testing/test_capture.py index 0f64e9c73d8..ffbc440e3bf 100644 --- a/testing/test_capture.py +++ b/testing/test_capture.py @@ -76,7 +76,7 @@ def test_capturing_basic_api(self, method) -> None: assert outerr == ("", "") print("hello") capman.suspend_global_capture() - out, err = capman.read_global_capture() + out, _err = capman.read_global_capture() if method == "no": assert old == (sys.stdout, sys.stderr, sys.stdin) else: @@ -84,7 +84,7 @@ def test_capturing_basic_api(self, method) -> None: capman.resume_global_capture() print("hello") capman.suspend_global_capture() - out, err = capman.read_global_capture() + out, _err = capman.read_global_capture() if method != "no": assert out == "hello\n" capman.stop_global_capturing() @@ -563,7 +563,7 @@ def test_hello(capfd): @pytest.mark.parametrize("nl", ("\n", "\r\n", "\r")) def test_cafd_preserves_newlines(self, capfd, nl) -> None: print("test", end=nl) - out, err = capfd.readouterr() + out, _err = capfd.readouterr() assert out.endswith(nl) def test_capfdbinary(self, pytester: Pytester) -> None: @@ -1153,7 +1153,7 @@ def test_capture_results_accessible_by_attribute(self) -> None: def test_capturing_readouterr_unicode(self) -> None: with self.getcapture() as cap: print("hxąć") - out, err = cap.readouterr() + out, _err = cap.readouterr() assert out == "hxąć\n" def test_reset_twice_error(self) -> None: @@ -1185,8 +1185,8 @@ def test_capturing_error_recursive(self) -> None: print("cap1") with self.getcapture() as cap2: print("cap2") - out2, err2 = cap2.readouterr() - out1, err1 = cap1.readouterr() + out2, _err2 = cap2.readouterr() + out1, _err1 = cap1.readouterr() assert out1 == "cap1\n" assert out2 == "cap2\n" @@ -1231,8 +1231,8 @@ def test_capturing_error_recursive(self) -> None: print("cap1") with self.getcapture() as cap2: print("cap2") - out2, err2 = cap2.readouterr() - out1, err1 = cap1.readouterr() + out2, _err2 = cap2.readouterr() + out1, _err1 = cap1.readouterr() assert out1 == "cap1\ncap2\n" assert out2 == "cap2\n" diff --git a/testing/test_collection.py b/testing/test_collection.py index 2214c130a05..bb2fd82c898 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -243,20 +243,20 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # executing from rootdir only tests from `testpaths` directories # are collected - items, reprec = pytester.inline_genitems("-v") + items, _reprec = pytester.inline_genitems("-v") assert [x.name for x in items] == ["test_b", "test_c"] # check that explicitly passing directories in the command-line # collects the tests for dirname in ("a", "b", "c"): - items, reprec = pytester.inline_genitems(tmp_path.joinpath(dirname)) + items, _reprec = pytester.inline_genitems(tmp_path.joinpath(dirname)) assert [x.name for x in items] == [f"test_{dirname}"] # changing cwd to each subdirectory and running pytest without # arguments collects the tests in that directory normally for dirname in ("a", "b", "c"): monkeypatch.chdir(pytester.path.joinpath(dirname)) - items, reprec = pytester.inline_genitems() + items, _reprec = pytester.inline_genitems() assert [x.name for x in items] == [f"test_{dirname}"] def test_missing_permissions_on_unselected_directory_doesnt_crash( @@ -640,10 +640,10 @@ def test_collect_two_commandline_args(self, pytester: Pytester) -> None: def test_serialization_byid(self, pytester: Pytester) -> None: pytester.makepyfile("def test_func(): pass") - items, hookrec = pytester.inline_genitems() + items, _hookrec = pytester.inline_genitems() assert len(items) == 1 (item,) = items - items2, hookrec = pytester.inline_genitems(item.nodeid) + items2, _hookrec = pytester.inline_genitems(item.nodeid) (item2,) = items2 assert item2.name == item.name assert item2.path == item.path @@ -673,7 +673,7 @@ def test_collect_parametrized_order(self, pytester: Pytester) -> None: def test_param(i): ... """ ) - items, hookrec = pytester.inline_genitems(f"{p}::test_param") + items, _hookrec = pytester.inline_genitems(f"{p}::test_param") assert len(items) == 3 assert [item.nodeid for item in items] == [ "test_collect_parametrized_order.py::test_param[0]", @@ -732,7 +732,7 @@ def test_2(): """ ) shutil.copy(p, p.parent / (p.stem + "2" + ".py")) - items, reprec = pytester.inline_genitems(p.parent) + items, _reprec = pytester.inline_genitems(p.parent) assert len(items) == 4 for numi, i in enumerate(items): for numj, j in enumerate(items): @@ -758,7 +758,7 @@ def testmethod_two(self, arg0): pass """ ) - items, reprec = pytester.inline_genitems(p) + items, _reprec = pytester.inline_genitems(p) assert len(items) == 4 assert items[0].name == "testone" assert items[1].name == "testmethod_one" @@ -786,7 +786,7 @@ def test_classmethod(cls) -> None: pass """ ) - items, reprec = pytester.inline_genitems(p) + items, _reprec = pytester.inline_genitems(p) ids = [x.getmodpath() for x in items] # type: ignore[attr-defined] assert ids == ["TestCase.test_classmethod"] @@ -811,7 +811,7 @@ def test_y(self): pass """ ) - items, reprec = pytester.inline_genitems(p) + items, _reprec = pytester.inline_genitems(p) ids = [x.getmodpath() for x in items] # type: ignore[attr-defined] assert ids == ["MyTestSuite.x_test", "TestCase.test_y"] diff --git a/testing/test_doctest.py b/testing/test_doctest.py index f4ed976c839..415d3a24faa 100644 --- a/testing/test_doctest.py +++ b/testing/test_doctest.py @@ -33,24 +33,24 @@ def test_collect_testtextfile(self, pytester: Pytester): for x in (pytester.path, checkfile): # print "checking that %s returns custom items" % (x,) - items, reprec = pytester.inline_genitems(x) + items, _reprec = pytester.inline_genitems(x) assert len(items) == 1 assert isinstance(items[0], DoctestItem) assert isinstance(items[0].parent, DoctestTextfile) # Empty file has no items. - items, reprec = pytester.inline_genitems(w) + items, _reprec = pytester.inline_genitems(w) assert len(items) == 0 def test_collect_module_empty(self, pytester: Pytester): path = pytester.makepyfile(whatever="#") for p in (path, pytester.path): - items, reprec = pytester.inline_genitems(p, "--doctest-modules") + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") assert len(items) == 0 def test_collect_module_single_modulelevel_doctest(self, pytester: Pytester): path = pytester.makepyfile(whatever='""">>> pass"""') for p in (path, pytester.path): - items, reprec = pytester.inline_genitems(p, "--doctest-modules") + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") assert len(items) == 1 assert isinstance(items[0], DoctestItem) assert isinstance(items[0].parent, DoctestModule) @@ -64,7 +64,7 @@ def my_func(): """ ) for p in (path, pytester.path): - items, reprec = pytester.inline_genitems(p, "--doctest-modules") + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") assert len(items) == 2 assert isinstance(items[0], DoctestItem) assert isinstance(items[1], DoctestItem) @@ -97,7 +97,7 @@ def another(): }, ) for p in (path, pytester.path): - items, reprec = pytester.inline_genitems(p, "--doctest-modules") + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") assert len(items) == 2 assert isinstance(items[0], DoctestItem) assert isinstance(items[1], DoctestItem) @@ -840,7 +840,7 @@ def foo(x): return 'c' """ ) - items, reprec = pytester.inline_genitems(p, "--doctest-modules") + items, _reprec = pytester.inline_genitems(p, "--doctest-modules") reportinfo = items[0].reportinfo() assert reportinfo[1] == 1 diff --git a/testing/test_junitxml.py b/testing/test_junitxml.py index 4f9702149a8..a05a69c58ca 100644 --- a/testing/test_junitxml.py +++ b/testing/test_junitxml.py @@ -261,7 +261,7 @@ def test_pass(): pass """ ) - result, dom = run_and_parse(family=xunit_family) + _result, dom = run_and_parse(family=xunit_family) node = dom.get_first_by_tag("testsuite") node.assert_attr(hostname=platform.node()) @@ -276,7 +276,7 @@ def test_pass(): """ ) start_time = datetime.now(timezone.utc) - result, dom = run_and_parse(family=xunit_family) + _result, dom = run_and_parse(family=xunit_family) node = dom.get_first_by_tag("testsuite") timestamp = datetime.fromisoformat(node["timestamp"]) assert start_time <= timestamp < datetime.now(timezone.utc) @@ -298,7 +298,7 @@ def test_sleep(): timing.sleep(4) """ ) - result, dom = run_and_parse() + _result, dom = run_and_parse() node = dom.get_first_by_tag("testsuite") tnode = node.get_first_by_tag("testcase") val = tnode["time"] @@ -329,7 +329,7 @@ def test_foo(): pass """ ) - result, dom = run_and_parse("-o", f"junit_duration_report={duration_report}") + _result, dom = run_and_parse("-o", f"junit_duration_report={duration_report}") node = dom.get_first_by_tag("testsuite") tnode = node.get_first_by_tag("testcase") val = float(tnode["time"]) @@ -635,7 +635,7 @@ def test_fail(): assert 0, "An error" """ ) - result, dom = run_and_parse(family=xunit_family) + _result, dom = run_and_parse(family=xunit_family) node = dom.get_first_by_tag("testsuite") tnode = node.get_first_by_tag("testcase") fnode = tnode.get_first_by_tag("failure") @@ -752,7 +752,7 @@ def test_fail(): assert 0 """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") tnode = node.get_first_by_tag("testcase") @@ -777,7 +777,7 @@ def test_xpass(): pass """ ) - result, dom = run_and_parse(family=xunit_family) + _result, dom = run_and_parse(family=xunit_family) # assert result.ret node = dom.get_first_by_tag("testsuite") node.assert_attr(skipped=0, tests=1) @@ -796,7 +796,7 @@ def test_xpass(): pass """ ) - result, dom = run_and_parse(family=xunit_family) + _result, dom = run_and_parse(family=xunit_family) # assert result.ret node = dom.get_first_by_tag("testsuite") node.assert_attr(skipped=0, tests=1) @@ -849,7 +849,7 @@ def test_str_compare(): assert M1 == M2 """ ) - result, dom = run_and_parse() + _result, dom = run_and_parse() print(dom.toxml()) @pytest.mark.parametrize("junit_logging", ["no", "system-out"]) @@ -862,7 +862,7 @@ def test_pass(): print('hello-stdout') """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") pnode = node.get_first_by_tag("testcase") if junit_logging == "no": @@ -886,7 +886,7 @@ def test_pass(): sys.stderr.write('hello-stderr') """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") pnode = node.get_first_by_tag("testcase") if junit_logging == "no": @@ -915,7 +915,7 @@ def test_function(arg): pass """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") pnode = node.get_first_by_tag("testcase") if junit_logging == "no": @@ -945,7 +945,7 @@ def test_function(arg): pass """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") pnode = node.get_first_by_tag("testcase") if junit_logging == "no": @@ -976,7 +976,7 @@ def test_function(arg): sys.stdout.write('hello-stdout call') """ ) - result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") + _result, dom = run_and_parse("-o", f"junit_logging={junit_logging}") node = dom.get_first_by_tag("testsuite") pnode = node.get_first_by_tag("testcase") if junit_logging == "no": @@ -1326,7 +1326,7 @@ def test_record_with_same_name(record_property): record_property("foo", "baz") """ ) - result, dom = run_and_parse() + _result, dom = run_and_parse() node = dom.get_first_by_tag("testsuite") tnode = node.get_first_by_tag("testcase") psnode = tnode.get_first_by_tag("properties") @@ -1402,7 +1402,7 @@ def test_record({fixture_name}, other): """ ) - result, dom = run_and_parse(family=None) + result, _dom = run_and_parse(family=None) expected_lines = [] if fixture_name == "record_xml_attribute": expected_lines.append( diff --git a/testing/test_legacypath.py b/testing/test_legacypath.py index 72854e4e5c0..80936667835 100644 --- a/testing/test_legacypath.py +++ b/testing/test_legacypath.py @@ -12,10 +12,10 @@ def test_item_fspath(pytester: pytest.Pytester) -> None: pytester.makepyfile("def test_func(): pass") - items, hookrec = pytester.inline_genitems() + items, _hookrec = pytester.inline_genitems() assert len(items) == 1 (item,) = items - items2, hookrec = pytester.inline_genitems(item.nodeid) + items2, _hookrec = pytester.inline_genitems(item.nodeid) (item2,) = items2 assert item2.name == item.name assert item2.fspath == item.fspath diff --git a/testing/test_main.py b/testing/test_main.py index 3f173ec4e9f..bfe6cad93a9 100644 --- a/testing/test_main.py +++ b/testing/test_main.py @@ -262,7 +262,7 @@ def test_absolute_paths_are_resolved_correctly(self, invocation_path: Path) -> N # ensure full paths given in the command-line without the drive letter resolve # to the full path correctly (#7628) - drive, full_path_without_drive = os.path.splitdrive(full_path) + _drive, full_path_without_drive = os.path.splitdrive(full_path) assert resolve_collection_argument( invocation_path, full_path_without_drive ) == CollectionArgument( @@ -300,7 +300,7 @@ def test(fix): fn = pytester.path.joinpath("project/tests/dummy_test.py") assert fn.is_file() - drive, path = os.path.splitdrive(str(fn)) + _drive, path = os.path.splitdrive(str(fn)) result = pytester.runpytest(path, "-v") result.stdout.fnmatch_lines( diff --git a/testing/test_mark.py b/testing/test_mark.py index 1e51f9db18f..9c067c3e430 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -228,7 +228,7 @@ def test_two(): """ ) rec = pytester.inline_run("-m", expr) - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() passed_str = [x.nodeid.split("::")[-1] for x in passed] assert passed_str == expected_passed @@ -276,7 +276,7 @@ def test_three(): """ ) rec = pytester.inline_run("-m", expr) - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() passed_str = [x.nodeid.split("::")[-1] for x in passed] assert passed_str == expected_passed @@ -306,7 +306,7 @@ def test_nointer(): """ ) rec = pytester.inline_run("-m", expr) - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() passed_str = [x.nodeid.split("::")[-1] for x in passed] assert passed_str == expected_passed @@ -341,7 +341,7 @@ def test_2(): """ ) rec = pytester.inline_run("-k", expr) - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() passed_str = [x.nodeid.split("::")[-1] for x in passed] assert passed_str == expected_passed @@ -373,7 +373,7 @@ def test_func(arg): """ ) rec = pytester.inline_run("-k", expr) - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() passed_str = [x.nodeid.split("::")[-1] for x in passed] assert passed_str == expected_passed @@ -388,7 +388,7 @@ def test_func(arg): """ ) rec = pytester.inline_run() - passed, skipped, fail = rec.listoutcomes() + passed, _skipped, _fail = rec.listoutcomes() expected_id = "test_func[" + pytest.__name__ + "]" assert passed[0].nodeid.split("::")[-1] == expected_id @@ -537,7 +537,7 @@ def test_d(self): assert True """ ) - items, rec = pytester.inline_genitems(p) + items, _rec = pytester.inline_genitems(p) for item in items: print(item, item.keywords) assert [x for x in item.iter_markers() if x.name == "a"] @@ -560,7 +560,7 @@ class Test2(Base): def test_bar(self): pass """ ) - items, rec = pytester.inline_genitems(p) + items, _rec = pytester.inline_genitems(p) self.assert_markers(items, test_foo=("a", "b"), test_bar=("a",)) def test_mark_should_not_pass_to_siebling_class(self, pytester: Pytester) -> None: @@ -583,7 +583,7 @@ class TestOtherSub(TestBase): """ ) - items, rec = pytester.inline_genitems(p) + items, _rec = pytester.inline_genitems(p) base_item, sub_item, sub_item_other = items print(items, [x.nodeid for x in items]) # new api segregates @@ -611,7 +611,7 @@ class Test2(Base2): def test_bar(self): pass """ ) - items, rec = pytester.inline_genitems(p) + items, _rec = pytester.inline_genitems(p) self.assert_markers(items, test_foo=("a", "b", "c"), test_bar=("a", "b", "d")) def test_mark_closest(self, pytester: Pytester) -> None: @@ -630,7 +630,7 @@ def test_has_inherited(self): """ ) - items, rec = pytester.inline_genitems(p) + items, _rec = pytester.inline_genitems(p) has_own, has_inherited = items has_own_marker = has_own.get_closest_marker("c") has_inherited_marker = has_inherited.get_closest_marker("c") @@ -826,7 +826,7 @@ def test_method_one(self): def check(keyword, name): reprec = pytester.inline_run("-s", "-k", keyword, file_test) - passed, skipped, failed = reprec.listoutcomes() + _passed, _skipped, failed = reprec.listoutcomes() assert len(failed) == 1 assert failed[0].nodeid.split("::")[-1] == name assert len(reprec.getcalls("pytest_deselected")) == 1 @@ -869,7 +869,7 @@ def pytest_pycollect_makeitem(name): ) reprec = pytester.inline_run(p.parent, "-s", "-k", keyword) print("keyword", repr(keyword)) - passed, skipped, failed = reprec.listoutcomes() + passed, _skipped, _failed = reprec.listoutcomes() assert len(passed) == 1 assert passed[0].nodeid.endswith("test_2") dlist = reprec.getcalls("pytest_deselected") @@ -885,7 +885,7 @@ def test_one(): """ ) reprec = pytester.inline_run("-k", "mykeyword", p) - passed, skipped, failed = reprec.countoutcomes() + _passed, _skipped, failed = reprec.countoutcomes() assert failed == 1 @pytest.mark.xfail diff --git a/testing/test_nodes.py b/testing/test_nodes.py index f5f21e9775c..de7875ca427 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -24,10 +24,10 @@ def test_node_direct_construction_deprecated() -> None: with pytest.raises( OutcomeException, match=( - "Direct construction of _pytest.nodes.Node has been deprecated, please " - "use _pytest.nodes.Node.from_parent.\nSee " - "https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent" - " for more details." + r"Direct construction of _pytest\.nodes\.Node has been deprecated, please " + r"use _pytest\.nodes\.Node\.from_parent.\nSee " + r"https://docs\.pytest\.org/en/stable/deprecations\.html#node-construction-changed-to-node-from-parent" + r" for more details\." ), ): nodes.Node(None, session=None) # type: ignore[arg-type] diff --git a/testing/test_pathlib.py b/testing/test_pathlib.py index 65a4117812f..0880c355557 100644 --- a/testing/test_pathlib.py +++ b/testing/test_pathlib.py @@ -1624,7 +1624,7 @@ def find_spec( return None # Setup directories without configuring sys.path. - models_py, algorithms_py = self.setup_directories( + models_py, _algorithms_py = self.setup_directories( tmp_path, monkeypatch=None, pytester=pytester ) com_root_1 = tmp_path / "src/dist1/com" diff --git a/testing/test_pluginmanager.py b/testing/test_pluginmanager.py index db85124bf0d..24700c07c80 100644 --- a/testing/test_pluginmanager.py +++ b/testing/test_pluginmanager.py @@ -436,7 +436,7 @@ def test_preparse_args(self, pytestpm: PytestPluginManager) -> None: # Handles -p without following arg (when used without argparse). pytestpm.consider_preparse(["-p"]) - with pytest.raises(UsageError, match="^plugin main cannot be disabled$"): + with pytest.raises(UsageError, match=r"^plugin main cannot be disabled$"): pytestpm.consider_preparse(["-p", "no:main"]) def test_plugin_prevent_register(self, pytestpm: PytestPluginManager) -> None: diff --git a/testing/test_pytester.py b/testing/test_pytester.py index a5ac8a91b8d..5e2e22f111b 100644 --- a/testing/test_pytester.py +++ b/testing/test_pytester.py @@ -799,7 +799,7 @@ def test_pytester_makefile_dot_prefixes_extension_with_warning( ) -> None: with pytest.raises( ValueError, - match="pytester.makefile expects a file extension, try .foo.bar instead of foo.bar", + match=r"pytester\.makefile expects a file extension, try \.foo\.bar instead of foo\.bar", ): pytester.makefile("foo.bar", "") diff --git a/testing/test_reports.py b/testing/test_reports.py index 5ffbde563b6..5c44ec72841 100644 --- a/testing/test_reports.py +++ b/testing/test_reports.py @@ -317,8 +317,8 @@ def check_longrepr(longrepr: ExceptionChainRepr) -> None: assert longrepr.sections == [("title", "contents", "=")] assert len(longrepr.chain) == 2 entry1, entry2 = longrepr.chain - tb1, fileloc1, desc1 = entry1 - tb2, fileloc2, desc2 = entry2 + tb1, _fileloc1, desc1 = entry1 + tb2, _fileloc2, desc2 = entry2 assert "ValueError('value error')" in str(tb1) assert "RuntimeError('runtime error')" in str(tb2) @@ -375,8 +375,8 @@ def check_longrepr(longrepr: object) -> None: assert isinstance(longrepr, ExceptionChainRepr) assert len(longrepr.chain) == 2 entry1, entry2 = longrepr.chain - tb1, fileloc1, desc1 = entry1 - tb2, fileloc2, desc2 = entry2 + tb1, fileloc1, _desc1 = entry1 + tb2, fileloc2, _desc2 = entry2 assert "RemoteTraceback" in str(tb1) assert "ValueError: value error" in str(tb2) diff --git a/testing/test_session.py b/testing/test_session.py index ba904916033..e3db9a1b690 100644 --- a/testing/test_session.py +++ b/testing/test_session.py @@ -66,7 +66,7 @@ def test_raises_doesnt(): pytest.raises(ValueError, int, "3") """ ) - passed, skipped, failed = reprec.listoutcomes() + _passed, _skipped, failed = reprec.listoutcomes() assert len(failed) == 1 out = failed[0].longrepr.reprcrash.message # type: ignore[union-attr] assert "DID NOT RAISE" in out diff --git a/testing/test_skipping.py b/testing/test_skipping.py index 3a3b4057e45..558e3656524 100644 --- a/testing/test_skipping.py +++ b/testing/test_skipping.py @@ -1304,7 +1304,7 @@ def pytest_collect_file(file_path, parent): """ ) result = pytester.inline_run() - passed, skipped, failed = result.listoutcomes() + _passed, skipped, failed = result.listoutcomes() assert not failed xfailed = [r for r in skipped if hasattr(r, "wasxfail")] assert xfailed @@ -1378,7 +1378,7 @@ def pytest_collect_file(file_path, parent): """ ) result = pytester.inline_run() - passed, skipped, failed = result.listoutcomes() + _passed, skipped, failed = result.listoutcomes() assert not failed xfailed = [r for r in skipped if hasattr(r, "wasxfail")] assert xfailed @@ -1406,7 +1406,7 @@ def test_fail(): def test_importorskip() -> None: with pytest.raises( pytest.skip.Exception, - match="^could not import 'doesnotexist': No module named .*", + match=r"^could not import 'doesnotexist': No module named .*", ): pytest.importorskip("doesnotexist") diff --git a/testing/test_tmpdir.py b/testing/test_tmpdir.py index 016588a143d..363172110d3 100644 --- a/testing/test_tmpdir.py +++ b/testing/test_tmpdir.py @@ -386,7 +386,7 @@ def test_cleanup_lock_create(self, tmp_path): d = tmp_path.joinpath("test") d.mkdir() lockfile = create_cleanup_lock(d) - with pytest.raises(OSError, match="cannot create lockfile in .*"): + with pytest.raises(OSError, match=r"cannot create lockfile in .*"): create_cleanup_lock(d) lockfile.unlink() diff --git a/testing/test_unittest.py b/testing/test_unittest.py index 56224c08228..ece8183b5e3 100644 --- a/testing/test_unittest.py +++ b/testing/test_unittest.py @@ -1374,7 +1374,7 @@ def test_cleanup_called_exactly_once(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert failed == 0 assert passed == 3 @@ -1398,7 +1398,7 @@ def test_cleanup_called_exactly_once(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert failed == 1 assert passed == 1 @@ -1426,7 +1426,7 @@ def test_cleanup_called_exactly_once(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, _failed = reprec.countoutcomes() assert passed == 3 @@ -1449,7 +1449,7 @@ def test_cleanup_called_the_right_number_of_times(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert failed == 0 assert passed == 3 @@ -1474,7 +1474,7 @@ def test_cleanup_called_the_right_number_of_times(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert failed == 2 assert passed == 1 @@ -1500,7 +1500,7 @@ def test_cleanup_called_the_right_number_of_times(): """ ) reprec = pytester.inline_run(testpath) - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert failed == 2 assert passed == 1 @@ -1614,7 +1614,7 @@ def test_it(self): """ ) reprec = pytester.inline_run() - passed, skipped, failed = reprec.countoutcomes() + passed, _skipped, failed = reprec.countoutcomes() assert passed == 1 assert failed == 1 assert reprec.ret == 1 diff --git a/testing/test_warning_types.py b/testing/test_warning_types.py index 7cbc4703c26..81d8785733c 100644 --- a/testing/test_warning_types.py +++ b/testing/test_warning_types.py @@ -43,7 +43,7 @@ def test(): @pytest.mark.filterwarnings("error") def test_warn_explicit_for_annotates_errors_with_location(): - with pytest.raises(Warning, match="(?m)test\n at .*raises.py:\\d+"): + with pytest.raises(Warning, match=r"(?m)test\n at .*raises.py:\d+"): warning_types.warn_explicit_for( pytest.raises, # type: ignore[arg-type] warning_types.PytestWarning("test"), From 5d1366a4b2d337c48d7c45ea59168de6bb0e9a8f Mon Sep 17 00:00:00 2001 From: jakkdl Date: Sat, 20 Sep 2025 08:11:31 -0300 Subject: [PATCH 3/4] Fix type failures in raises.py and add tests Fix typing failures after mypy 1.18 update. --- src/_pytest/raises.py | 6 +++--- testing/python/raises_group.py | 10 ++++++++++ testing/test_monkeypatch.py | 2 +- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/_pytest/raises.py b/src/_pytest/raises.py index 9066779a8af..2a53a5a148a 100644 --- a/src/_pytest/raises.py +++ b/src/_pytest/raises.py @@ -452,7 +452,7 @@ def _parse_exc( issubclass(origin_exc, BaseExceptionGroup) and exc_type in (BaseException, Any) ): - if not isinstance(exc, Exception): + if not issubclass(origin_exc, ExceptionGroup): self.is_baseexception = True return cast(type[BaseExcT_1], origin_exc) else: @@ -465,9 +465,9 @@ def _parse_exc( ) # unclear if the Type/ValueError distinction is even helpful here msg = f"expected exception must be {expected}, not " - if isinstance(exc, type): + if isinstance(exc, type): # type: ignore[unreachable] raise ValueError(msg + f"{exc.__name__!r}") - if isinstance(exc, BaseException): + if isinstance(exc, BaseException): # type: ignore[unreachable] raise TypeError(msg + f"an exception instance ({type(exc).__name__})") raise TypeError(msg + repr(type(exc).__name__)) diff --git a/testing/python/raises_group.py b/testing/python/raises_group.py index 6b9f98201dd..e911ba52cbc 100644 --- a/testing/python/raises_group.py +++ b/testing/python/raises_group.py @@ -1325,6 +1325,16 @@ def test_annotated_group() -> None: with RaisesExc(BaseExceptionGroup[BaseException]): raise BaseExceptionGroup("", [KeyboardInterrupt()]) + # assure AbstractRaises.is_baseexception is set properly + assert ( + RaisesGroup(ExceptionGroup[Exception]).expected_type() + == "ExceptionGroup(ExceptionGroup)" + ) + assert ( + RaisesGroup(BaseExceptionGroup[BaseException]).expected_type() + == "BaseExceptionGroup(BaseExceptionGroup)" + ) + def test_tuples() -> None: # raises has historically supported one of several exceptions being raised diff --git a/testing/test_monkeypatch.py b/testing/test_monkeypatch.py index 0e992e298ec..edf3169bed5 100644 --- a/testing/test_monkeypatch.py +++ b/testing/test_monkeypatch.py @@ -417,7 +417,7 @@ def test_context() -> None: with monkeypatch.context() as m: m.setattr(functools, "partial", 3) assert not inspect.isclass(functools.partial) - assert inspect.isclass(functools.partial) # type:ignore[unreachable] + assert inspect.isclass(functools.partial) def test_context_classmethod() -> None: From b290db3838506ca03c3bda82dc7cb7c7a4b31a43 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Sat, 20 Sep 2025 10:26:45 -0300 Subject: [PATCH 4/4] test workflow: use an explicit matrix variable instead of an inline value zizmor complains that the previous approach was considered "obfuscation". Using a matrix value instead as suggested in https://github.com/pytest-dev/pytest/pull/13733#issuecomment-3314979079. --- .github/workflows/test.yml | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 41a0773e1af..61125eb2761 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -112,6 +112,7 @@ jobs: python: "3.10" os: windows-latest tox_env: "py310-pluggymain-pylib-xdist" + xfail: true - name: "windows-py310-xdist" python: "3.10" @@ -132,6 +133,7 @@ jobs: python: "3.13" os: windows-latest tox_env: "py313" + xfail: true - name: "windows-py314" python: "3.14" @@ -167,11 +169,13 @@ jobs: python: "3.10" os: ubuntu-latest tox_env: "py310-pluggymain-pylib-xdist" + xfail: true - name: "ubuntu-py310-freeze" python: "3.10" os: ubuntu-latest tox_env: "py310-freeze" + xfail: true - name: "ubuntu-py310-xdist" python: "3.10" @@ -195,6 +199,7 @@ jobs: os: ubuntu-latest tox_env: "py313-pexpect" use_coverage: true + xfail: true - name: "ubuntu-py314" python: "3.14" @@ -212,6 +217,7 @@ jobs: python: "3.10" os: macos-latest tox_env: "py310-xdist" + xfail: true - name: "macos-py312" python: "3.12" @@ -222,6 +228,7 @@ jobs: python: "3.13" os: macos-latest tox_env: "py313-xdist" + xfail: true - name: "macos-py314" python: "3.14" @@ -240,25 +247,7 @@ jobs: tox_env: "doctesting" use_coverage: true - continue-on-error: >- - ${{ - contains( - fromJSON( - '[ - "windows-py310-pluggy", - "windows-py313", - "ubuntu-py310-pluggy", - "ubuntu-py310-freeze", - "ubuntu-py313", - "macos-py310", - "macos-py313" - ]' - ), - matrix.name - ) - && true - || false - }} + continue-on-error: ${{ matrix.xfail && true || false }} steps: - uses: actions/checkout@v5