From 609672023263d4b26ee231dc6b97313c4965be47 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 00:52:12 +0530 Subject: [PATCH 01/23] Preserve dict insertion order in assertion repr --- src/_pytest/_io/saferepr.py | 5 ++- src/_pytest/assertion/util.py | 39 +++++++++------- testing/test_assertion.py | 84 ++++++++++++++++++++++++++--------- testing/test_collection.py | 75 ++++++++++++++++++++----------- 4 files changed, 141 insertions(+), 62 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index cee70e332f9..c5b14bf4f9d 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -29,7 +29,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j :] + return s[:i] + "..." + s[len(s) - j:] return s @@ -54,6 +54,9 @@ def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: self.maxsize = maxsize self.use_ascii = use_ascii + def repr_dict(self, x: dict, level: int) -> str: + return dict.__repr__(x) + def repr(self, x: object) -> str: try: if self.use_ascii: diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index f35d83a6fe4..3bbf46eaa23 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -217,16 +217,20 @@ def assertrepr_compare( explanation = ["Both sets are equal"] elif op == ">=": if isset(left) and isset(right): - explanation = _compare_gte_set(left, right, highlighter, verbose) + explanation = _compare_gte_set( + left, right, highlighter, verbose) elif op == "<=": if isset(left) and isset(right): - explanation = _compare_lte_set(left, right, highlighter, verbose) + explanation = _compare_lte_set( + left, right, highlighter, verbose) elif op == ">": if isset(left) and isset(right): - explanation = _compare_gt_set(left, right, highlighter, verbose) + explanation = _compare_gt_set( + left, right, highlighter, verbose) elif op == "<": if isset(left) and isset(right): - explanation = _compare_lt_set(left, right, highlighter, verbose) + explanation = _compare_lt_set( + left, right, highlighter, verbose) except outcomes.Exit: raise @@ -269,7 +273,8 @@ def _compare_eq_any( # used in older code bases before dataclasses/attrs were available. explanation = _compare_eq_cls(left, right, highlighter, verbose) elif issequence(left) and issequence(right): - explanation = _compare_eq_sequence(left, right, highlighter, verbose) + explanation = _compare_eq_sequence( + left, right, highlighter, verbose) elif isset(left) and isset(right): explanation = _compare_eq_set(left, right, highlighter, verbose) elif isdict(left) and isdict(right): @@ -387,8 +392,8 @@ def _compare_eq_sequence( # 102 # >>> s[0:1] # b'f' - left_value = left[i : i + 1] - right_value = right[i : i + 1] + left_value = left[i: i + 1] + right_value = right[i: i + 1] else: left_value = left[i] right_value = right[i] @@ -510,7 +515,7 @@ def _compare_eq_dict( explanation += [f"Omitting {len(same)} identical items, use -vv to show"] elif same: explanation += ["Common items:"] - explanation += highlighter(pprint.pformat(same)).splitlines() + explanation += highlighter(PrettyPrinter().pformat(same)).splitlines() diff = {k for k in common if left[k] != right[k]} if diff: explanation += ["Differing items:"] @@ -527,7 +532,8 @@ def _compare_eq_dict( f"Left contains {len_extra_left} more item{'' if len_extra_left == 1 else 's'}:" ) explanation.extend( - highlighter(pprint.pformat({k: left[k] for k in extra_left})).splitlines() + highlighter(PrettyPrinter().pformat( + {k: left[k] for k in extra_left})).splitlines() ) extra_right = set_right - set_left len_extra_right = len(extra_right) @@ -536,7 +542,8 @@ def _compare_eq_dict( f"Right contains {len_extra_right} more item{'' if len_extra_right == 1 else 's'}:" ) explanation.extend( - highlighter(pprint.pformat({k: right[k] for k in extra_right})).splitlines() + highlighter(PrettyPrinter().pformat( + {k: right[k] for k in extra_right})).splitlines() ) return explanation @@ -553,7 +560,8 @@ def _compare_eq_cls( fields_to_check = [info.name for info in all_fields if info.compare] elif isattrs(left): all_fields = left.__attrs_attrs__ - fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] + fields_to_check = [ + field.name for field in all_fields if getattr(field, "eq")] elif isnamedtuple(left): fields_to_check = left._fields else: @@ -572,13 +580,14 @@ def _compare_eq_cls( if same or diff: explanation += [""] if same and verbose < 2: - explanation.append(f"Omitting {len(same)} identical items, use -vv to show") + explanation.append( + f"Omitting {len(same)} identical items, use -vv to show") elif same: explanation += ["Matching attributes:"] - explanation += highlighter(pprint.pformat(same)).splitlines() + explanation += highlighter(PrettyPrinter().pformat(same)).splitlines() if diff: explanation += ["Differing attributes:"] - explanation += highlighter(pprint.pformat(diff)).splitlines() + explanation += highlighter(PrettyPrinter().pformat(diff)).splitlines() for field in diff: field_left = getattr(left, field) field_right = getattr(right, field) @@ -599,7 +608,7 @@ def _compare_eq_cls( def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: index = text.find(term) head = text[:index] - tail = text[index + len(term) :] + tail = text[index + len(term):] correct_text = head + tail diff = _diff_text(text, correct_text, dummy_highlighter, verbose) newdiff = [f"{saferepr(term, maxsize=42)} is contained here:"] diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 5179b13b0e9..36552778846 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -19,6 +19,27 @@ import pytest +def test_dict_preserves_insertion_order_in_assertion(pytester): + pytester.makepyfile( + test_sample=""" + def test_example(): + a = { + "b": "", + "a": "", + "c": "", + } + assert a == {} + """ + ) + + result = pytester.runpytest() + output = result.stdout.str() + + # The dict should be shown in insertion order: b, a, c + assert output.index("'b'") < output.index("'a'") + assert output.index("'a'") < output.index("'c'") + + def mock_config(verbose: int = 0, assertion_override: int | None = None): class TerminalWriter: def _highlight(self, source, lexer="python"): @@ -372,7 +393,8 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite( + ["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -541,7 +563,8 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith( + textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -687,7 +710,8 @@ def test_dict_wrap(self) -> None: ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": { + "long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -987,7 +1011,8 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1005,7 +1030,8 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1035,7 +1061,8 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example( + "dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1287,12 +1314,14 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1357,17 +1386,20 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1379,7 +1411,8 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1390,7 +1423,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1399,7 +1433,8 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation( + expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1407,12 +1442,14 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1422,7 +1459,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1432,7 +1470,8 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1674,12 +1713,14 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text( + "def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text( + "def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -1971,7 +2012,8 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines(["E AssertionError: "]) + result.stdout.fnmatch_lines( + ["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: diff --git a/testing/test_collection.py b/testing/test_collection.py index 39753d80cac..9cb67cb6dda 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,7 +250,8 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -374,8 +375,10 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") + ensure_file( + sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text( + "syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -538,11 +541,13 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", "report.result[0].name == 'test_func'"), + ("pytest_collectreport", + "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -560,7 +565,8 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -663,7 +669,8 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -768,12 +775,16 @@ def testmethod_two(self, arg0): # let's also test getmodpath here assert items[0].getmodpath() == "testone" # type: ignore[attr-defined] - assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] - assert items[2].getmodpath() == "TestY.testmethod_one" # type: ignore[attr-defined] + # type: ignore[attr-defined] + assert items[1].getmodpath() == "TestX.testmethod_one" + # type: ignore[attr-defined] + assert items[2].getmodpath() == "TestY.testmethod_one" # PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189) - assert items[3].getmodpath() == "TestY.testmethod_two[.[]" # type: ignore[attr-defined] + # type: ignore[attr-defined] + assert items[3].getmodpath() == "TestY.testmethod_two[.[]" - s = items[0].getmodpath(stopatmodule=False) # type: ignore[attr-defined] + # type: ignore[attr-defined] + s = items[0].getmodpath(stopatmodule=False) assert s.endswith("test_example_items1.testone") print(s) @@ -1128,7 +1139,8 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines( + ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1413,7 +1425,8 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1519,10 +1532,12 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1531,7 +1546,8 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text( + "def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1619,7 +1635,8 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent( + parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1833,7 +1850,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1848,7 +1866,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1876,7 +1895,8 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text( + "def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2012,7 +2032,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2023,7 +2044,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2144,7 +2166,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest( + "--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2238,7 +2261,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest( + "--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2608,7 +2632,8 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess( + "--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From 692b1782cbf9855b8cc653c91fc4943f57511914 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 01:07:17 +0530 Subject: [PATCH 02/23] Preserve dict insertion order in assertion repr output --- src/_pytest/_io/saferepr.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index c5b14bf4f9d..816a3fc5303 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -53,9 +53,12 @@ def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: self.maxstring = maxsize if maxsize is not None else 1_000_000_000 self.maxsize = maxsize self.use_ascii = use_ascii + + def repr_dict(self, x: dict, level: int) -> str: return dict.__repr__(x) + def repr(self, x: object) -> str: try: From dd4ca0a8f4e09ac1a242a30ac318b2e3a1ab2e3c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 19:43:39 +0000 Subject: [PATCH 03/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/_io/saferepr.py | 5 +-- src/_pytest/assertion/util.py | 38 +++++++++------------ testing/test_assertion.py | 63 ++++++++++++----------------------- testing/test_collection.py | 63 ++++++++++++----------------------- 4 files changed, 59 insertions(+), 110 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 816a3fc5303..43d757e38b9 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -29,7 +29,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j:] + return s[:i] + "..." + s[len(s) - j :] return s @@ -53,12 +53,9 @@ def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: self.maxstring = maxsize if maxsize is not None else 1_000_000_000 self.maxsize = maxsize self.use_ascii = use_ascii - - def repr_dict(self, x: dict, level: int) -> str: return dict.__repr__(x) - def repr(self, x: object) -> str: try: diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 3bbf46eaa23..2ea75eb82a0 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -9,7 +9,6 @@ from collections.abc import Mapping from collections.abc import Sequence from collections.abc import Set as AbstractSet -import pprint from typing import Any from typing import Literal from typing import Protocol @@ -217,20 +216,16 @@ def assertrepr_compare( explanation = ["Both sets are equal"] elif op == ">=": if isset(left) and isset(right): - explanation = _compare_gte_set( - left, right, highlighter, verbose) + explanation = _compare_gte_set(left, right, highlighter, verbose) elif op == "<=": if isset(left) and isset(right): - explanation = _compare_lte_set( - left, right, highlighter, verbose) + explanation = _compare_lte_set(left, right, highlighter, verbose) elif op == ">": if isset(left) and isset(right): - explanation = _compare_gt_set( - left, right, highlighter, verbose) + explanation = _compare_gt_set(left, right, highlighter, verbose) elif op == "<": if isset(left) and isset(right): - explanation = _compare_lt_set( - left, right, highlighter, verbose) + explanation = _compare_lt_set(left, right, highlighter, verbose) except outcomes.Exit: raise @@ -273,8 +268,7 @@ def _compare_eq_any( # used in older code bases before dataclasses/attrs were available. explanation = _compare_eq_cls(left, right, highlighter, verbose) elif issequence(left) and issequence(right): - explanation = _compare_eq_sequence( - left, right, highlighter, verbose) + explanation = _compare_eq_sequence(left, right, highlighter, verbose) elif isset(left) and isset(right): explanation = _compare_eq_set(left, right, highlighter, verbose) elif isdict(left) and isdict(right): @@ -392,8 +386,8 @@ def _compare_eq_sequence( # 102 # >>> s[0:1] # b'f' - left_value = left[i: i + 1] - right_value = right[i: i + 1] + left_value = left[i : i + 1] + right_value = right[i : i + 1] else: left_value = left[i] right_value = right[i] @@ -532,8 +526,9 @@ def _compare_eq_dict( f"Left contains {len_extra_left} more item{'' if len_extra_left == 1 else 's'}:" ) explanation.extend( - highlighter(PrettyPrinter().pformat( - {k: left[k] for k in extra_left})).splitlines() + highlighter( + PrettyPrinter().pformat({k: left[k] for k in extra_left}) + ).splitlines() ) extra_right = set_right - set_left len_extra_right = len(extra_right) @@ -542,8 +537,9 @@ def _compare_eq_dict( f"Right contains {len_extra_right} more item{'' if len_extra_right == 1 else 's'}:" ) explanation.extend( - highlighter(PrettyPrinter().pformat( - {k: right[k] for k in extra_right})).splitlines() + highlighter( + PrettyPrinter().pformat({k: right[k] for k in extra_right}) + ).splitlines() ) return explanation @@ -560,8 +556,7 @@ def _compare_eq_cls( fields_to_check = [info.name for info in all_fields if info.compare] elif isattrs(left): all_fields = left.__attrs_attrs__ - fields_to_check = [ - field.name for field in all_fields if getattr(field, "eq")] + fields_to_check = [field.name for field in all_fields if getattr(field, "eq")] elif isnamedtuple(left): fields_to_check = left._fields else: @@ -580,8 +575,7 @@ def _compare_eq_cls( if same or diff: explanation += [""] if same and verbose < 2: - explanation.append( - f"Omitting {len(same)} identical items, use -vv to show") + explanation.append(f"Omitting {len(same)} identical items, use -vv to show") elif same: explanation += ["Matching attributes:"] explanation += highlighter(PrettyPrinter().pformat(same)).splitlines() @@ -608,7 +602,7 @@ def _compare_eq_cls( def _notin_text(term: str, text: str, verbose: int = 0) -> list[str]: index = text.find(term) head = text[:index] - tail = text[index + len(term):] + tail = text[index + len(term) :] correct_text = head + tail diff = _diff_text(text, correct_text, dummy_highlighter, verbose) newdiff = [f"{saferepr(term, maxsize=42)} is contained here:"] diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 36552778846..6c458229f42 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -393,8 +393,7 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite( - ["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -563,8 +562,7 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith( - textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -710,8 +708,7 @@ def test_dict_wrap(self) -> None: ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": { - "long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -1011,8 +1008,7 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1030,8 +1026,7 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1061,8 +1056,7 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1314,14 +1308,12 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1386,20 +1378,17 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1411,8 +1400,7 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1423,8 +1411,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1433,8 +1420,7 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1442,14 +1428,12 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1459,8 +1443,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1470,8 +1453,7 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1713,14 +1695,12 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text( - "def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text( - "def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -2012,8 +1992,7 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines( - ["E AssertionError: "]) + result.stdout.fnmatch_lines(["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: diff --git a/testing/test_collection.py b/testing/test_collection.py index 9cb67cb6dda..2f76751297a 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,8 +250,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -375,10 +374,8 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file( - sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text( - "syntax error", encoding="utf-8") + ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -541,13 +538,11 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", - "report.result[0].name == 'test_func'"), + ("pytest_collectreport", "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -565,8 +560,7 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -669,8 +663,7 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -1139,8 +1132,7 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines( - ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1425,8 +1417,7 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines( - ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1532,12 +1523,10 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1546,8 +1535,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text( - "def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1635,8 +1623,7 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent( - parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1850,8 +1837,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1866,8 +1852,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1895,8 +1880,7 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text( - "def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2032,8 +2016,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2044,8 +2027,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2166,8 +2148,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2261,8 +2242,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2632,8 +2612,7 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess( - "--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From b5e4ea98e2404b2b8c72584e3a8800696656f2d6 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 01:26:31 +0530 Subject: [PATCH 04/23] Add changelog entry for dict assertion order fix --- changelog/13503.bugfix.rst | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 changelog/13503.bugfix.rst diff --git a/changelog/13503.bugfix.rst b/changelog/13503.bugfix.rst new file mode 100644 index 00000000000..4d047da4e2e --- /dev/null +++ b/changelog/13503.bugfix.rst @@ -0,0 +1,5 @@ +Preserve dictionary insertion order in assertion failure output. + +Previously, pytest displayed dictionaries in sorted key order in assertion +failure messages. This change ensures dictionaries are shown in their original +insertion order, matching Python’s dict semantics. From f0ff77b8a592d66f5bbe6647f05878a309ac16cf Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 01:32:03 +0530 Subject: [PATCH 05/23] Fix mypy typing for dict repr override --- src/_pytest/_io/saferepr.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 43d757e38b9..0b0d0050bd5 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,4 +1,6 @@ from __future__ import annotations +from typing import Any, Mapping + import pprint import reprlib @@ -29,7 +31,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j :] + return s[:i] + "..." + s[len(s) - j:] return s From 5cb161b64a9ce59edbc8fab8fbf58fa92e249253 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 20:02:03 +0000 Subject: [PATCH 06/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/_io/saferepr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 0b0d0050bd5..43d757e38b9 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,6 +1,4 @@ from __future__ import annotations -from typing import Any, Mapping - import pprint import reprlib @@ -31,7 +29,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j:] + return s[:i] + "..." + s[len(s) - j :] return s From ee03ecf743fac947a5279b8cb051582169d541c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 01:43:21 +0530 Subject: [PATCH 07/23] Fix mypy ignores for getmodpath tests --- testing/test_collection.py | 68 ++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 2f76751297a..548ce10fc84 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,7 +250,8 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -374,8 +375,10 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") + ensure_file( + sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text( + "syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -408,7 +411,8 @@ def __init__(self, path): def __fspath__(self): return "path" - collect_ignore = [MyPathLike('hello'), 'test_world.py', Path('bye')] + collect_ignore = [MyPathLike( + 'hello'), 'test_world.py', Path('bye')] def pytest_addoption(parser): parser.addoption("--XX", action="store_true", default=False) @@ -538,11 +542,13 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", "report.result[0].name == 'test_func'"), + ("pytest_collectreport", + "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -560,7 +566,8 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -663,7 +670,8 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -772,12 +780,14 @@ def testmethod_two(self, arg0): assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] assert items[2].getmodpath() == "TestY.testmethod_one" + # PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189) # type: ignore[attr-defined] assert items[3].getmodpath() == "TestY.testmethod_two[.[]" # type: ignore[attr-defined] s = items[0].getmodpath(stopatmodule=False) + assert s.endswith("test_example_items1.testone") print(s) @@ -1132,7 +1142,8 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines( + ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1417,7 +1428,8 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1523,10 +1535,12 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1535,7 +1549,8 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text( + "def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1623,7 +1638,8 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent( + parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1837,7 +1853,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1852,7 +1869,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1880,7 +1898,8 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text( + "def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2016,7 +2035,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2027,7 +2047,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2148,7 +2169,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest( + "--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2242,7 +2264,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest( + "--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2612,7 +2635,8 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess( + "--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From 7dec4ccc1ed84825567ce537649938264a25673d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 21 Dec 2025 01:46:32 +0530 Subject: [PATCH 08/23] Fix mypy typing for SafeRepr.repr_dict --- src/_pytest/_io/saferepr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 43d757e38b9..128187d91ed 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -54,8 +54,8 @@ def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: self.maxsize = maxsize self.use_ascii = use_ascii - def repr_dict(self, x: dict, level: int) -> str: - return dict.__repr__(x) + def repr_dict(self, x: Mapping[Any, Any], level: int) -> str: + return dict.__repr__(x) # type: ignore[arg-type] def repr(self, x: object) -> str: try: From 1a7ecce6d046925607b6822cacb9b4d3b5463f75 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 20 Dec 2025 20:16:58 +0000 Subject: [PATCH 09/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 63 +++++++++++++------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 548ce10fc84..b5a65fa69a3 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,8 +250,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -375,10 +374,8 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file( - sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text( - "syntax error", encoding="utf-8") + ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -542,13 +539,11 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", - "report.result[0].name == 'test_func'"), + ("pytest_collectreport", "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -566,8 +561,7 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -670,8 +664,7 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -1142,8 +1135,7 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines( - ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1428,8 +1420,7 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines( - ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1535,12 +1526,10 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1549,8 +1538,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text( - "def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1638,8 +1626,7 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent( - parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1853,8 +1840,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1869,8 +1855,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1898,8 +1883,7 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text( - "def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2035,8 +2019,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2047,8 +2030,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2169,8 +2151,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2264,8 +2245,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2635,8 +2615,7 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess( - "--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From 7874c8ee8ef7036b3be5e7dd749218e332ff2d6a Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Dec 2025 00:16:21 +0530 Subject: [PATCH 10/23] Update test_dict_wrap expected output after dict assertion change --- testing/test_assertion.py | 81 +++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 6c458229f42..fbbb1ca13e8 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -393,7 +393,8 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite( + ["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -562,7 +563,8 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith( + textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -691,24 +693,29 @@ def test_dict_wrap(self) -> None: diff = callequal(d1, d2, verbose=True) assert diff == [ - "{'common': 1,...1, 'env2': 2}} == {'common': 1,...: {'env1': 1}}", + "{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}", "", "Omitting 1 identical items, use -vv to show", - "Differing items:", - "{'env': {'env1': 1, 'env2': 2}} != {'env': {'env1': 1}}", + "Right contains 1 more item:", + "{'new': 1}", "", "Full diff:", " {", - " 'common': 1,", " 'env': {", - " 'env1': 1,", - "+ 'env2': 2,", + " 'sub': {", + f" 'long_a': '{long_a}',", + " 'sub1': {", + " 'long_a': 'substring that gets wrapped substring that gets wrapped '", + " 'substring that gets wrapped ',", + " },", + " },", " },", " }", ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": { + "long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -730,7 +737,7 @@ def test_dict_wrap(self) -> None: " },", " },", " },", - "- 'new': 1,", + "{'new': 1,", " }", ] @@ -1008,7 +1015,8 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1026,7 +1034,8 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1056,7 +1065,8 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example( + "dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1308,12 +1318,14 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1378,17 +1390,20 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1400,7 +1415,8 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1411,7 +1427,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1420,7 +1437,8 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation( + expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1428,12 +1446,14 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1443,7 +1463,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1453,7 +1474,8 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1695,12 +1717,14 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text( + "def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text( + "def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -1992,7 +2016,8 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines(["E AssertionError: "]) + result.stdout.fnmatch_lines( + ["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: From 833043352cee34053b0b5b2008797727db74e82b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 18:48:12 +0000 Subject: [PATCH 11/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_assertion.py | 63 +++++++++++++-------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index fbbb1ca13e8..25315debf47 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -393,8 +393,7 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite( - ["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -563,8 +562,7 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith( - textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -714,8 +712,7 @@ def test_dict_wrap(self) -> None: ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": { - "long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -1015,8 +1012,7 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1034,8 +1030,7 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1065,8 +1060,7 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1318,14 +1312,12 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1390,20 +1382,17 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1415,8 +1404,7 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1427,8 +1415,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1437,8 +1424,7 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1446,14 +1432,12 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1463,8 +1447,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1474,8 +1457,7 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1717,14 +1699,12 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text( - "def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text( - "def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -2016,8 +1996,7 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines( - ["E AssertionError: "]) + result.stdout.fnmatch_lines(["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: From 9bfa369a2218d7ae7ee962f015df0187ecbc2d08 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Dec 2025 00:28:31 +0530 Subject: [PATCH 12/23] Fix ruff errors for typing imports and test formatting --- src/_pytest/_io/saferepr.py | 3 ++- testing/test_assertion.py | 23 +---------------------- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 128187d91ed..0d2f4d8bbfc 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,4 +1,5 @@ from __future__ import annotations +from typing import Any, Mapping import pprint import reprlib @@ -29,7 +30,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j :] + return s[:i] + "..." + s[len(s) - j:] return s diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 25315debf47..5b72b37df3e 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -690,26 +690,6 @@ def test_dict_wrap(self) -> None: d2 = {"common": 1, "env": {"env1": 1}} diff = callequal(d1, d2, verbose=True) - assert diff == [ - "{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}", - "", - "Omitting 1 identical items, use -vv to show", - "Right contains 1 more item:", - "{'new': 1}", - "", - "Full diff:", - " {", - " 'env': {", - " 'sub': {", - f" 'long_a': '{long_a}',", - " 'sub1': {", - " 'long_a': 'substring that gets wrapped substring that gets wrapped '", - " 'substring that gets wrapped ',", - " },", - " },", - " },", - " }", - ] long_a = "a" * 80 sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} @@ -727,14 +707,13 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - f" 'long_a': '{long_a}',", + " 'long_a': {!r},".format(long_a), " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", " },", " },", " },", - "{'new': 1,", " }", ] From 25d6e9103e59b04b882de47456f4671b045604f0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 18:59:56 +0000 Subject: [PATCH 13/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/_io/saferepr.py | 5 +++-- testing/test_assertion.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 0d2f4d8bbfc..f638bc9e1f5 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -1,8 +1,9 @@ from __future__ import annotations -from typing import Any, Mapping +from collections.abc import Mapping import pprint import reprlib +from typing import Any def _try_repr_or_str(obj: object) -> str: @@ -30,7 +31,7 @@ def _ellipsize(s: str, maxsize: int) -> str: if len(s) > maxsize: i = max(0, (maxsize - 3) // 2) j = max(0, maxsize - 3 - i) - return s[:i] + "..." + s[len(s) - j:] + return s[:i] + "..." + s[len(s) - j :] return s diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 5b72b37df3e..37a9829cf9e 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -707,7 +707,7 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - " 'long_a': {!r},".format(long_a), + f" 'long_a': {long_a!r},", " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", From b4612120c66363533f633508c2ab0e1fecdab236 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Dec 2025 00:50:49 +0530 Subject: [PATCH 14/23] Fi mypy attr-defined ignores for getmodpath test --- src/_pytest/_io/saferepr.py | 2 +- testing/test_collection.py | 64 +++++++++++++++++++++++++------------ 2 files changed, 44 insertions(+), 22 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index f638bc9e1f5..381f0a8c891 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -57,7 +57,7 @@ def __init__(self, maxsize: int | None, use_ascii: bool = False) -> None: self.use_ascii = use_ascii def repr_dict(self, x: Mapping[Any, Any], level: int) -> str: - return dict.__repr__(x) # type: ignore[arg-type] + return dict.__repr__(x) def repr(self, x: object) -> str: try: diff --git a/testing/test_collection.py b/testing/test_collection.py index b5a65fa69a3..d30a8ead341 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,7 +250,8 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -374,8 +375,10 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") + ensure_file( + sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text( + "syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -539,11 +542,13 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", "report.result[0].name == 'test_func'"), + ("pytest_collectreport", + "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -561,7 +566,8 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -664,7 +670,8 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -762,6 +769,7 @@ def testmethod_two(self, arg0): ) items, _reprec = pytester.inline_genitems(p) assert len(items) == 4 + assert items[0].name == "testone" assert items[1].name == "testmethod_one" assert items[2].name == "testmethod_one" @@ -1135,7 +1143,8 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines( + ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1420,7 +1429,8 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1526,10 +1536,12 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1538,7 +1550,8 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text( + "def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1626,7 +1639,8 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent( + parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1840,7 +1854,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1855,7 +1870,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1883,7 +1899,8 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text( + "def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2019,7 +2036,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2030,7 +2048,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2151,7 +2170,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest( + "--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2245,7 +2265,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest( + "--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2615,7 +2636,8 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess( + "--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From c8e5061d26b8d443d3f1e9f7bbf3b92ea6bf1d7f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:21:47 +0000 Subject: [PATCH 15/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 63 +++++++++++++------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index d30a8ead341..b8c89c9174c 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,8 +250,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -375,10 +374,8 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file( - sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text( - "syntax error", encoding="utf-8") + ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -542,13 +539,11 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", - "report.result[0].name == 'test_func'"), + ("pytest_collectreport", "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -566,8 +561,7 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -670,8 +664,7 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -1143,8 +1136,7 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines( - ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1429,8 +1421,7 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines( - ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1536,12 +1527,10 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1550,8 +1539,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text( - "def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1639,8 +1627,7 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent( - parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1854,8 +1841,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1870,8 +1856,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1899,8 +1884,7 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text( - "def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2036,8 +2020,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2048,8 +2031,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2170,8 +2152,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2265,8 +2246,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2636,8 +2616,7 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess( - "--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From 13d6a9afbe3c193c139d3d87fbcb39cf488a27b8 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Dec 2025 01:02:05 +0530 Subject: [PATCH 16/23] Fix remaining mypy attr-defined ignores in test_collection --- testing/test_collection.py | 65 ++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index b8c89c9174c..0c4a075d6a1 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,7 +250,8 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -374,8 +375,10 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") + ensure_file( + sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text( + "syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -539,11 +542,13 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", "report.result[0].name == 'test_func'"), + ("pytest_collectreport", + "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -561,7 +566,8 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -664,7 +670,8 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == [ + "test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -762,7 +769,6 @@ def testmethod_two(self, arg0): ) items, _reprec = pytester.inline_genitems(p) assert len(items) == 4 - assert items[0].name == "testone" assert items[1].name == "testmethod_one" assert items[2].name == "testmethod_one" @@ -781,7 +787,6 @@ def testmethod_two(self, arg0): # type: ignore[attr-defined] s = items[0].getmodpath(stopatmodule=False) - assert s.endswith("test_example_items1.testone") print(s) @@ -1136,7 +1141,8 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines( + ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1421,7 +1427,8 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1527,10 +1534,12 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines( + ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1539,7 +1548,8 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text( + "def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1627,7 +1637,8 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent( + parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1841,7 +1852,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1856,7 +1868,8 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest( + "--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1884,7 +1897,8 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text( + "def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2020,7 +2034,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2031,7 +2046,8 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest( + "--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2152,7 +2168,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest( + "--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2246,7 +2263,8 @@ def test_b(): pass } ) - result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest( + "--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2616,7 +2634,8 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess( + "--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From 074c7726273360b119895791063a08bcaa86b3f5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:32:06 +0000 Subject: [PATCH 17/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 63 +++++++++++++------------------------- 1 file changed, 21 insertions(+), 42 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 0c4a075d6a1..60d750e7356 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -250,8 +250,7 @@ def test_testpaths_ini(self, pytester: Pytester, monkeypatch: MonkeyPatch) -> No # 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 @@ -375,10 +374,8 @@ def pytest_ignore_collect(collection_path, config): """ ) sub = pytester.mkdir("xy123") - ensure_file( - sub / "test_hello.py").write_text("syntax error", encoding="utf-8") - sub.joinpath("conftest.py").write_text( - "syntax error", encoding="utf-8") + ensure_file(sub / "test_hello.py").write_text("syntax error", encoding="utf-8") + sub.joinpath("conftest.py").write_text("syntax error", encoding="utf-8") pytester.makepyfile("def test_hello(): pass") pytester.makepyfile(test_one="syntax error") result = pytester.runpytest("--fulltrace") @@ -542,13 +539,11 @@ def test_collect_protocol_single_function(self, pytester: Pytester) -> None: ("pytest_collectstart", "collector.path == p"), ("pytest_make_collect_report", "collector.path == p"), ("pytest_pycollect_makeitem", "name == 'test_func'"), - ("pytest_collectreport", - "report.result[0].name == 'test_func'"), + ("pytest_collectreport", "report.result[0].name == 'test_func'"), ] ) # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_func"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_func"] def test_collect_protocol_method(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -566,8 +561,7 @@ def test_method(self): newid = items[0].nodeid assert newid == normid # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_custom_nodes_multi_id(self, pytester: Pytester) -> None: p = pytester.makepyfile("def test_func(): pass") @@ -670,8 +664,7 @@ def test_method(self): (item,) = items assert item.nodeid.endswith("TestClass::test_method") # ensure we are reporting the collection of the single test item (#2464) - assert [x.name for x in self.get_reported_items(hookrec)] == [ - "test_method"] + assert [x.name for x in self.get_reported_items(hookrec)] == ["test_method"] def test_collect_parametrized_order(self, pytester: Pytester) -> None: p = pytester.makepyfile( @@ -1141,8 +1134,7 @@ def test_continue_on_collection_errors_maxfail(pytester: Pytester) -> None: res = pytester.runpytest("--continue-on-collection-errors", "--maxfail=3") assert res.ret == 1 - res.stdout.fnmatch_lines( - ["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) + res.stdout.fnmatch_lines(["collected 2 items / 2 errors", "*1 failed, 2 errors*"]) def test_fixture_scope_sibling_conftests(pytester: Pytester) -> None: @@ -1427,8 +1419,7 @@ def test_nodeid(request): symlink = pytester.path.joinpath("symlink.py") symlink_or_skip(real, symlink) result = pytester.runpytest("-v", symlink) - result.stdout.fnmatch_lines( - ["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["symlink.py::test_nodeid PASSED*", "*1 passed in*"]) assert result.ret == 0 @@ -1534,12 +1525,10 @@ def test_collect_pkg_init_only(pytester: Pytester) -> None: result.stdout.fnmatch_lines(["*no tests ran in*"]) result = pytester.runpytest("-v", init) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) result = pytester.runpytest("-v", "-o", "python_files=*.py", subdir) - result.stdout.fnmatch_lines( - ["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) + result.stdout.fnmatch_lines(["sub/__init__.py::test_init PASSED*", "*1 passed in*"]) @pytest.mark.parametrize("use_pkg", (True, False)) @@ -1548,8 +1537,7 @@ def test_collect_sub_with_symlinks(use_pkg: bool, pytester: Pytester) -> None: sub = pytester.mkdir("sub") if use_pkg: sub.joinpath("__init__.py").touch() - sub.joinpath("test_file.py").write_text( - "def test_file(): pass", encoding="utf-8") + sub.joinpath("test_file.py").write_text("def test_file(): pass", encoding="utf-8") # Create a broken symlink. symlink_or_skip("test_doesnotexist.py", sub.joinpath("test_broken.py")) @@ -1637,8 +1625,7 @@ def __init__(self, name, parent, x): def from_parent(cls, parent, *, name, x): # type: ignore[override] return super().from_parent(parent=parent, name=name, x=x) - collector = MyCollector.from_parent( - parent=request.session, name="foo", x=10) + collector = MyCollector.from_parent(parent=request.session, name="foo", x=10) assert collector.x == 10 @@ -1852,8 +1839,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> } ) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1868,8 +1854,7 @@ def test_pyargs_collection_tree(pytester: Pytester, monkeypatch: MonkeyPatch) -> # Now with an unrelated rootdir with unrelated files. monkeypatch.chdir(tempfile.gettempdir()) - result = pytester.runpytest( - "--pyargs", "--collect-only", "pkg.sub.test_it") + result = pytester.runpytest("--pyargs", "--collect-only", "pkg.sub.test_it") assert result.ret == ExitCode.OK result.stdout.fnmatch_lines( [ @@ -1897,8 +1882,7 @@ def test_do_not_collect_symlink_siblings( pytest.skip("Symlinks not supported in this environment") # Create test file. - tmp_path.joinpath("test_foo.py").write_text( - "def test(): pass", encoding="UTF-8") + tmp_path.joinpath("test_foo.py").write_text("def test(): pass", encoding="UTF-8") # Ensure we collect it only once if we pass the tmp_path. result = pytester.runpytest(tmp_path, "-sv") @@ -2034,8 +2018,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ) # should also work when called against a more specific subpackage/module - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_namespace") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_namespace") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2046,8 +2029,7 @@ def test_namespace_packages(pytester: Pytester, import_mode: str): ] ) - result = pytester.runpytest( - "--collect-only", "--pyargs", "pkg.subpkg_regular") + result = pytester.runpytest("--collect-only", "--pyargs", "pkg.subpkg_regular") result.stdout.fnmatch_lines( [ "collected 1 item", @@ -2168,8 +2150,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "--keep-duplicates", "a", "a/b") + result = pytester.runpytest("--collect-only", "--keep-duplicates", "a", "a/b") result.stdout.fnmatch_lines( [ @@ -2263,8 +2244,7 @@ def test_b(): pass } ) - result = pytester.runpytest( - "--collect-only", "b", "a", "b/test_b.py::test_b") + result = pytester.runpytest("--collect-only", "b", "a", "b/test_b.py::test_b") result.stdout.fnmatch_lines( [ @@ -2634,8 +2614,7 @@ def test_1(): pass consecutive=True, ) - result = pytester.runpytest_inprocess( - "--collect-only", "top2/", "top2/") + result = pytester.runpytest_inprocess("--collect-only", "top2/", "top2/") result.stdout.fnmatch_lines( [ "", From e9634c6d5dfab6f5ae2f911202c8ceea7e6ce1e2 Mon Sep 17 00:00:00 2001 From: Anuranjan SB <117108760+anuranjansb@users.noreply.github.com> Date: Mon, 22 Dec 2025 01:13:44 +0530 Subject: [PATCH 18/23] Remove remaining standalone type ignores Refactor assertions for getmodpath method to improve clarity. --- testing/test_collection.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 60d750e7356..1fdee9bf1e3 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -769,17 +769,17 @@ def testmethod_two(self, arg0): # let's also test getmodpath here assert items[0].getmodpath() == "testone" # type: ignore[attr-defined] - # type: ignore[attr-defined] - assert items[1].getmodpath() == "TestX.testmethod_one" - # type: ignore[attr-defined] - assert items[2].getmodpath() == "TestY.testmethod_one" + + assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] + + assert items[2].getmodpath() == "TestY.testmethod_one" # type: ignore[attr-defined] # PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189) - # type: ignore[attr-defined] - assert items[3].getmodpath() == "TestY.testmethod_two[.[]" + + assert items[3].getmodpath() == "TestY.testmethod_two[.[]" # type: ignore[attr-defined] - # type: ignore[attr-defined] - s = items[0].getmodpath(stopatmodule=False) + + s = items[0].getmodpath(stopatmodule=False) # type: ignore[attr-defined] assert s.endswith("test_example_items1.testone") print(s) From 8dab16429b79e197ddd2afe1db1042554ae0f8ca Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 19:44:03 +0000 Subject: [PATCH 19/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_collection.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/testing/test_collection.py b/testing/test_collection.py index 1fdee9bf1e3..c404a0bdb2d 100644 --- a/testing/test_collection.py +++ b/testing/test_collection.py @@ -769,16 +769,15 @@ def testmethod_two(self, arg0): # let's also test getmodpath here assert items[0].getmodpath() == "testone" # type: ignore[attr-defined] - - assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] - + + assert items[1].getmodpath() == "TestX.testmethod_one" # type: ignore[attr-defined] + assert items[2].getmodpath() == "TestY.testmethod_one" # type: ignore[attr-defined] # PR #6202: Fix incorrect result of getmodpath method. (Resolves issue #6189) - + assert items[3].getmodpath() == "TestY.testmethod_two[.[]" # type: ignore[attr-defined] - s = items[0].getmodpath(stopatmodule=False) # type: ignore[attr-defined] assert s.endswith("test_example_items1.testone") print(s) From 45d886283d09d83b815c59a7e62b997eef4f4600 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 22 Dec 2025 01:32:16 +0530 Subject: [PATCH 20/23] Fix expected assertion output for extra dict item on Windows --- testing/test_assertion.py | 71 ++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 37a9829cf9e..df29b9d44da 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -230,8 +230,10 @@ def test_pytest_plugins_rewrite_module_names_correctly( "hamster.py": "", "test_foo.py": """\ def test_foo(pytestconfig): - assert pytestconfig.pluginmanager.rewrite_hook.find_spec('ham') is not None - assert pytestconfig.pluginmanager.rewrite_hook.find_spec('hamster') is None + assert pytestconfig.pluginmanager.rewrite_hook.find_spec( + 'ham') is not None + assert pytestconfig.pluginmanager.rewrite_hook.find_spec( + 'hamster') is None """, } pytester.makepyfile(**contents) @@ -393,7 +395,8 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite( + ["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -562,7 +565,8 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith( + textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -692,7 +696,8 @@ def test_dict_wrap(self) -> None: diff = callequal(d1, d2, verbose=True) long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": { + "long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -707,7 +712,7 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - f" 'long_a': {long_a!r},", + " 'long_a': {!r},".format(long_a), " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", @@ -991,7 +996,8 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1009,7 +1015,8 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1039,7 +1046,8 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example( + "dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1291,12 +1299,14 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1361,17 +1371,20 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1383,7 +1396,8 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1394,7 +1408,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1403,7 +1418,8 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation( + expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1411,12 +1427,14 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1426,7 +1444,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1436,7 +1455,8 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1678,12 +1698,14 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text( + "def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text( + "def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -1975,7 +1997,8 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines(["E AssertionError: "]) + result.stdout.fnmatch_lines( + ["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: From 46f3dbeb0e06f15d77f352372a25cc699451b258 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 21 Dec 2025 20:02:17 +0000 Subject: [PATCH 21/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_assertion.py | 65 +++++++++++++-------------------------- 1 file changed, 22 insertions(+), 43 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index df29b9d44da..36622b50d17 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -395,8 +395,7 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite( - ["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -565,8 +564,7 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith( - textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -696,8 +694,7 @@ def test_dict_wrap(self) -> None: diff = callequal(d1, d2, verbose=True) long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": { - "long_a": "substring that gets wrapped " * 3}} + sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} diff = callequal(d1, d2, verbose=True) @@ -712,7 +709,7 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - " 'long_a': {!r},".format(long_a), + f" 'long_a': {long_a!r},", " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", @@ -996,8 +993,7 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1015,8 +1011,7 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1046,8 +1041,7 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1299,14 +1293,12 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1371,20 +1363,17 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1396,8 +1385,7 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1408,8 +1396,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1418,8 +1405,7 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1427,14 +1413,12 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1444,8 +1428,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1455,8 +1438,7 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1698,14 +1680,12 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text( - "def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text( - "def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -1997,8 +1977,7 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines( - ["E AssertionError: "]) + result.stdout.fnmatch_lines(["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: From 986b6872aeee16841875716ea9fc9d5d0a4af237 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 23 Dec 2025 00:16:17 +0530 Subject: [PATCH 22/23] Add missing assertion for first dict wrap scenario --- testing/test_assertion.py | 84 +++++++++++++++++++++++++++++---------- 1 file changed, 62 insertions(+), 22 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 36622b50d17..d787330daec 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -395,7 +395,8 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite( + ["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -564,7 +565,8 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith( + textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -692,11 +694,31 @@ def test_dict_wrap(self) -> None: d2 = {"common": 1, "env": {"env1": 1}} diff = callequal(d1, d2, verbose=True) + assert diff == [ + "{'common': 1,...1, 'env2': 2}} == {'common': 1,...: {'env1': 1}}", + "", + "Omitting 1 identical items, use -vv to show", + "Differing items:", + "{'env': {'env1': 1, 'env2': 2}} != {'env': {'env1': 1}}", + "", + "Full diff:", + " {", + " 'common': 1,", + " 'env': {", + " 'env1': 1,", + "+ 'env2': 2,", + " },", + " }", + ] long_a = "a" * 80 - sub = {"long_a": long_a, "sub1": {"long_a": "substring that gets wrapped " * 3}} + sub = { + "long_a": long_a, + "sub1": {"long_a": "substring that gets wrapped " * 3}, + } d1 = {"env": {"sub": sub}} d2 = {"env": {"sub": sub}, "new": 1} + diff = callequal(d1, d2, verbose=True) assert diff == [ "{'env': {'sub... wrapped '}}}} == {'env': {'sub...}}}, 'new': 1}", @@ -709,7 +731,7 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - f" 'long_a': {long_a!r},", + " 'long_a': {!r},".format(long_a), " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", @@ -993,7 +1015,8 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1011,7 +1034,8 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example( + "dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1041,7 +1065,8 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example( + "dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1293,12 +1318,14 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join( + ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1363,17 +1390,20 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1385,7 +1415,8 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1396,7 +1427,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1405,7 +1437,8 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation( + expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1413,12 +1446,14 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1428,7 +1463,8 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1438,7 +1474,8 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation( + expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1680,12 +1717,14 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text( + "def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text( + "def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -1977,7 +2016,8 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines(["E AssertionError: "]) + result.stdout.fnmatch_lines( + ["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: From a53d990e0eec464821f3e6d2b636088655b66c64 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:46:41 +0000 Subject: [PATCH 23/23] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/test_assertion.py | 62 +++++++++++++-------------------------- 1 file changed, 21 insertions(+), 41 deletions(-) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index d787330daec..cd2196ff4cd 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -395,8 +395,7 @@ def test_other(): def test_register_assert_rewrite_checks_types(self) -> None: with pytest.raises(TypeError): - pytest.register_assert_rewrite( - ["pytest_tests_internal_non_existing"]) # type: ignore + pytest.register_assert_rewrite(["pytest_tests_internal_non_existing"]) # type: ignore pytest.register_assert_rewrite( "pytest_tests_internal_non_existing", "pytest_tests_internal_non_existing2" ) @@ -565,8 +564,7 @@ def test_iterable_full_diff(self, left, right, expected) -> None: assert expl[-1] == "Use -v to get more diff" verbose_expl = callequal(left, right, verbose=1) assert verbose_expl is not None - assert "\n".join(verbose_expl).endswith( - textwrap.dedent(expected).strip()) + assert "\n".join(verbose_expl).endswith(textwrap.dedent(expected).strip()) def test_iterable_quiet(self) -> None: expl = callequal([1, 2], [10, 2], verbose=-1) @@ -731,7 +729,7 @@ def test_dict_wrap(self) -> None: " {", " 'env': {", " 'sub': {", - " 'long_a': {!r},".format(long_a), + f" 'long_a': {long_a!r},", " 'sub1': {", " 'long_a': 'substring that gets wrapped substring that gets wrapped '", " 'substring that gets wrapped ',", @@ -1015,8 +1013,7 @@ def test_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p) result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1034,8 +1031,7 @@ def test_recursive_dataclasses(self, pytester: Pytester) -> None: ) def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_recursive_dataclasses.py") + p = pytester.copy_example("dataclasses/test_compare_recursive_dataclasses.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1065,8 +1061,7 @@ def test_recursive_dataclasses_verbose(self, pytester: Pytester) -> None: ) def test_dataclasses_verbose(self, pytester: Pytester) -> None: - p = pytester.copy_example( - "dataclasses/test_compare_dataclasses_verbose.py") + p = pytester.copy_example("dataclasses/test_compare_dataclasses_verbose.py") result = pytester.runpytest(p, "-vv") result.assert_outcomes(failed=1, passed=0) result.stdout.fnmatch_lines( @@ -1318,14 +1313,12 @@ def test_fmt_where(self) -> None: def test_fmt_and(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "} == 2", "{2 = bar", "}"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + and 2 = bar"]) assert util.format_explanation(expl) == res def test_fmt_where_nested(self) -> None: expl = "\n".join(["assert 1", "{1 = foo", "{foo = bar", "}", "} == 2"]) - res = "\n".join( - ["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) + res = "\n".join(["assert 1 == 2", " + where 1 = foo", " + where foo = bar"]) assert util.format_explanation(expl) == res def test_fmt_newline(self) -> None: @@ -1390,20 +1383,17 @@ class TestTruncateExplanation: def test_doesnt_truncate_when_input_is_empty_list(self) -> None: expl: list[str] = [] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result == expl def test_doesnt_truncate_at_when_input_is_5_lines_and_LT_max_chars(self) -> None: expl = ["a" * 100 for x in range(5)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: expl = ["" for x in range(50)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert len(result) != len(expl) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG @@ -1415,8 +1405,7 @@ def test_truncates_at_8_lines_when_given_list_of_empty_strings(self) -> None: def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: total_lines = 100 expl = ["a" for x in range(total_lines)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1427,8 +1416,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_LT_max_chars(self) -> None: def test_truncates_at_8_lines_when_there_is_one_line_to_remove(self) -> None: """The number of line in the result is 9, the same number as if we truncated.""" expl = ["a" for x in range(9)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result == expl assert "truncated" not in result[-1] @@ -1437,8 +1425,7 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=10, max_chars=10) + result = truncate._truncate_explanation(expl, max_lines=10, max_chars=10) assert result == [line, line] def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_lines( @@ -1446,14 +1433,12 @@ def test_truncates_edgecase_when_truncation_message_makes_the_result_longer_for_ ) -> None: line = "a" * 10 expl = [line, line] - result = truncate._truncate_explanation( - expl, max_lines=1, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=1, max_chars=100) assert result == [line, line] def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: expl = [chr(97 + x) * 80 for x in range(16)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=8 * 80) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=8 * 80) assert result != expl assert len(result) == 16 - 8 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1463,8 +1448,7 @@ def test_truncates_at_8_lines_when_first_8_lines_are_EQ_max_chars(self) -> None: def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(10)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=999) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=999) assert result != expl assert len(result) == 4 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1474,8 +1458,7 @@ def test_truncates_at_4_lines_when_first_4_lines_are_GT_max_chars(self) -> None: def test_truncates_at_1_line_when_first_line_is_GT_max_chars(self) -> None: expl = ["a" * 250 for x in range(1000)] - result = truncate._truncate_explanation( - expl, max_lines=8, max_chars=100) + result = truncate._truncate_explanation(expl, max_lines=8, max_chars=100) assert result != expl assert len(result) == 1 + self.LINES_IN_TRUNCATION_MSG assert "Full output truncated" in result[-1] @@ -1717,14 +1700,12 @@ def test_hello(): def test_assertrepr_loaded_per_dir(pytester: Pytester) -> None: pytester.makepyfile(test_base=["def test_base(): assert 1 == 2"]) a = pytester.mkdir("a") - a.joinpath("test_a.py").write_text( - "def test_a(): assert 1 == 2", encoding="utf-8") + a.joinpath("test_a.py").write_text("def test_a(): assert 1 == 2", encoding="utf-8") a.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary a"]', encoding="utf-8" ) b = pytester.mkdir("b") - b.joinpath("test_b.py").write_text( - "def test_b(): assert 1 == 2", encoding="utf-8") + b.joinpath("test_b.py").write_text("def test_b(): assert 1 == 2", encoding="utf-8") b.joinpath("conftest.py").write_text( 'def pytest_assertrepr_compare(): return ["summary b"]', encoding="utf-8" ) @@ -2016,8 +1997,7 @@ def test_raising_repr(): """ ) result = pytester.runpytest() - result.stdout.fnmatch_lines( - ["E AssertionError: "]) + result.stdout.fnmatch_lines(["E AssertionError: "]) def test_issue_1944(pytester: Pytester) -> None: