From fe2f2067a1a9db19b675f2bdf1f42db52f426a63 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Wed, 19 Jun 2024 22:45:07 +0200 Subject: [PATCH 01/16] refactor: simplify bound method representation --- src/_pytest/_io/saferepr.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 5ace418227d..49f747dc2e1 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -2,6 +2,7 @@ import pprint import reprlib +from types import MethodType def _try_repr_or_str(obj: object) -> str: @@ -59,7 +60,17 @@ def repr(self, x: object) -> str: if self.use_ascii: s = ascii(x) else: - s = super().repr(x) + if isinstance(x, MethodType): + # for bound methods, skip redundant information + s = x.__name__ + else: + # if none of the mro classes have implemented __repr__ + # show class name + mro_classes = x.__class__.mro()[:-1] + if not any("__repr__" in cls.__dict__ for cls in mro_classes): + s = x.__class__.__name__ + else: + s = super().repr(x) except (KeyboardInterrupt, SystemExit): raise From c6d1b0b17f9cc162f3f71543643bf79d20b5e5e3 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 11:01:56 +0200 Subject: [PATCH 02/16] tests: add for bound method representation --- src/_pytest/_io/saferepr.py | 8 +------- testing/io/test_saferepr.py | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 49f747dc2e1..520d6c4d1b9 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -64,13 +64,7 @@ def repr(self, x: object) -> str: # for bound methods, skip redundant information s = x.__name__ else: - # if none of the mro classes have implemented __repr__ - # show class name - mro_classes = x.__class__.mro()[:-1] - if not any("__repr__" in cls.__dict__ for cls in mro_classes): - s = x.__class__.__name__ - else: - s = super().repr(x) + s = super().repr(x) except (KeyboardInterrupt, SystemExit): raise diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 075d40cdf44..a69f72544b9 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -192,3 +192,28 @@ def __repr__(self): assert saferepr_unlimited(A()).startswith( "<[ValueError(42) raised in repr()] A object at 0x" ) + + +class TestSafereprUnbounded: + class Help: + def __init__(self, i): + self.i = i + + def bound_method(self): + return self.i + + def test_saferepr_bound_method(self): + """saferepr() of a bound method should show only the method name""" + assert saferepr(self.Help(10).bound_method) == "bound_method" + + def test_saferepr_unbounded(self): + """saferepr() of an unbound method should still show the full information""" + obj = self.Help(10) + assert ( + saferepr(obj) + == f"" + ) + assert ( + saferepr(self.Help) + == f"" + ) From 0f28228973f936335fb40b7c4c1c3aa7926ad112 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 11:23:39 +0200 Subject: [PATCH 03/16] add change log for 389 --- AUTHORS | 1 + changelog/389.improvement.rst | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 changelog/389.improvement.rst diff --git a/AUTHORS b/AUTHORS index 347efad57b3..4d34d2ba9b4 100644 --- a/AUTHORS +++ b/AUTHORS @@ -149,6 +149,7 @@ Evgeny Seliverstov Fabian Sturm Fabien Zarifian Fabio Zadrozny +Farbod Ahmadian faph Felix Hofstätter Felix Nieuwenhuizen diff --git a/changelog/389.improvement.rst b/changelog/389.improvement.rst new file mode 100644 index 00000000000..d3f3af530b9 --- /dev/null +++ b/changelog/389.improvement.rst @@ -0,0 +1,38 @@ +The readability of assertion introspection of bound methods has been enhanced +-- by :user:`farbodahm`, :user:`webknjaz`, :user:`obestwalter`, :user:`flub` +and :user:`glyphack`. + +Previously it was something like this: + +.. code-block:: console + + =================================== FAILURES =================================== + _____________________________________ test _____________________________________ + + def test(): + > assert Help().fun() == 2 + E assert 1 == 2 + E + where 1 = >() + E + where > = .fun + E + where = Help() + + example.py:7: AssertionError + =========================== 1 failed in 0.03 seconds =========================== + + +And now it's like: + +.. code-block:: console + + =================================== FAILURES =================================== + _____________________________________ test _____________________________________ + + def test(): + > assert Help().fun() == 2 + E assert 1 == 2 + E + where 1 = fun() + E + where fun = .fun + E + where = Help() + + test_local.py:13: AssertionError + =========================== 1 failed in 0.03 seconds =========================== From 3d4c80e0abf9e52b584fef478153b0d6a51a1059 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 12:14:16 +0200 Subject: [PATCH 04/16] =?UTF-8?q?=F0=9F=A7=AA=F0=9F=9A=91=F0=9F=90=9B=20Ad?= =?UTF-8?q?just=20object=20ID=20repr=20@=20unbound=20saferepr=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Sviatoslav Sydorenko --- testing/io/test_saferepr.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index a69f72544b9..19df24738ce 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -196,22 +196,20 @@ def __repr__(self): class TestSafereprUnbounded: class Help: - def __init__(self, i): - self.i = i - def bound_method(self): - return self.i + pass def test_saferepr_bound_method(self): """saferepr() of a bound method should show only the method name""" - assert saferepr(self.Help(10).bound_method) == "bound_method" + assert saferepr(self.Help().bound_method) == "bound_method" def test_saferepr_unbounded(self): """saferepr() of an unbound method should still show the full information""" - obj = self.Help(10) + obj = self.Help() + obj_id = f"{id(obj) :x}".lstrip("0").lower() assert ( saferepr(obj) - == f"" + == f"" ) assert ( saferepr(self.Help) From e96ebb14dc4ff804db0e6a64b07f29cb88a720d5 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 15:05:53 +0200 Subject: [PATCH 05/16] fixup! tests: add for bound method representation --- testing/io/test_saferepr.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 19df24738ce..e330dd9af80 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -5,6 +5,7 @@ from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest +import re def test_simple_repr(): @@ -206,7 +207,9 @@ def test_saferepr_bound_method(self): def test_saferepr_unbounded(self): """saferepr() of an unbound method should still show the full information""" obj = self.Help() - obj_id = f"{id(obj) :x}".lstrip("0").lower() + # On windows, id adds leading zeros + obj_id = re.sub(r"^0+", "", f"{id(obj):x}").lower() + assert ( saferepr(obj) == f"" From eaf495566cc53852ee96b62c1afc28ee92eaa31e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:07:57 +0000 Subject: [PATCH 06/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/io/test_saferepr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index e330dd9af80..3af31824c2d 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,11 +1,11 @@ # mypy: allow-untyped-defs from __future__ import annotations +import re from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest -import re def test_simple_repr(): From c1091be526e2768f73be81ff4262c158888ad190 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 15:05:53 +0200 Subject: [PATCH 07/16] fixup! tests: add for bound method representation --- testing/io/test_saferepr.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 3af31824c2d..7dd56164aca 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -6,6 +6,7 @@ from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest +import re def test_simple_repr(): @@ -207,13 +208,11 @@ def test_saferepr_bound_method(self): def test_saferepr_unbounded(self): """saferepr() of an unbound method should still show the full information""" obj = self.Help() - # On windows, id adds leading zeros - obj_id = re.sub(r"^0+", "", f"{id(obj):x}").lower() - - assert ( - saferepr(obj) - == f"" + # using id() to fetch memory address fails on different platforms + pattern = re.compile( + r"", ) + assert pattern.match(saferepr(obj)) assert ( saferepr(self.Help) == f"" From 2f7f1e9cbacd1fd8dffea885a6bfa160e9aafcdf Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:38:54 +0000 Subject: [PATCH 08/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/io/test_saferepr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 7dd56164aca..99acaf68a16 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -6,7 +6,6 @@ from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest -import re def test_simple_repr(): From 6fd5e111307d1b77ea5daf1bd5bd898777d82ce9 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 15:05:53 +0200 Subject: [PATCH 09/16] fixup! tests: add for bound method representation --- testing/io/test_saferepr.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 99acaf68a16..cedad5e9a33 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -6,6 +6,7 @@ from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest +import re def test_simple_repr(): @@ -197,7 +198,7 @@ def __repr__(self): class TestSafereprUnbounded: class Help: - def bound_method(self): + def bound_method(self): # pragma: no cover pass def test_saferepr_bound_method(self): From ffca5d88a3da232a05e4fa80ddbd0ff92f225298 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 13:55:16 +0000 Subject: [PATCH 10/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/io/test_saferepr.py | 1 - 1 file changed, 1 deletion(-) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index cedad5e9a33..847a65fa3ba 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -6,7 +6,6 @@ from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited import pytest -import re def test_simple_repr(): From f45486cc605ad39526e39030b7b0a785a65aca08 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Thu, 20 Jun 2024 16:43:07 +0200 Subject: [PATCH 11/16] fixup! add change log for 389 --- changelog/389.improvement.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog/389.improvement.rst b/changelog/389.improvement.rst index d3f3af530b9..f8e2c19fde0 100644 --- a/changelog/389.improvement.rst +++ b/changelog/389.improvement.rst @@ -2,7 +2,7 @@ The readability of assertion introspection of bound methods has been enhanced -- by :user:`farbodahm`, :user:`webknjaz`, :user:`obestwalter`, :user:`flub` and :user:`glyphack`. -Previously it was something like this: +Earlier, it was like: .. code-block:: console From b9f9d255076a1f902ef1336ddcc99f6fd42b51f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 12:05:09 +0000 Subject: [PATCH 12/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- testing/io/test_saferepr.py | 1 + 1 file changed, 1 insertion(+) diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 847a65fa3ba..a4226522c03 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,5 +1,6 @@ # mypy: allow-untyped-defs from __future__ import annotations + import re from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE From a9eba91f7f50a6d6ec8bcc9151239e460b69f0f9 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Fri, 21 Jun 2024 17:32:46 +0200 Subject: [PATCH 13/16] refactor: move bound_method repr from global version to rewrite module --- src/_pytest/_io/saferepr.py | 7 +------ src/_pytest/assertion/rewrite.py | 28 ++++++++++++++++++++++------ testing/io/test_saferepr.py | 23 ----------------------- testing/test_assertrewrite.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 36 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 520d6c4d1b9..49769913783 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -60,12 +60,7 @@ def repr(self, x: object) -> str: if self.use_ascii: s = ascii(x) else: - if isinstance(x, MethodType): - # for bound methods, skip redundant information - s = x.__name__ - else: - s = super().repr(x) - + s = super().repr(x) except (KeyboardInterrupt, SystemExit): raise except BaseException as exc: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 442fc5d9f30..c3eeca87c0a 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -417,6 +417,10 @@ def _saferepr(obj: object) -> str: sequences, especially '\n{' and '\n}' are likely to be present in JSON reprs. """ + if isinstance(obj, types.MethodType): + # for bound methods, skip redundant information + return obj.__name__ + maxsize = _get_maxsize_for_saferepr(util._config) return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") @@ -1014,7 +1018,9 @@ def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: ] ): pytest_temp = self.variable() - self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] + self.variables_overwrite[self.scope][ + v.left.target.id + ] = v.left # type:ignore[assignment] v.left.target.id = pytest_temp self.push_format_context() res, expl = self.visit(v) @@ -1058,7 +1064,9 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( self.scope, {} ): - arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] + arg = self.variables_overwrite[self.scope][ + arg.id + ] # type:ignore[assignment] res, expl = self.visit(arg) arg_expls.append(expl) new_args.append(res) @@ -1066,7 +1074,9 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance( keyword.value, ast.Name ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] + keyword.value = self.variables_overwrite[self.scope][ + keyword.value.id + ] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: @@ -1103,9 +1113,13 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: if isinstance( comp.left, ast.Name ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] + comp.left = self.variables_overwrite[self.scope][ + comp.left.id + ] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] + self.variables_overwrite[self.scope][ + comp.left.target.id + ] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): left_expl = f"({left_expl})" @@ -1123,7 +1137,9 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: and next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] + self.variables_overwrite[self.scope][ + left_res.id + ] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): next_expl = f"({next_expl})" diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index a4226522c03..7a3066ac6a5 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -194,26 +194,3 @@ def __repr__(self): assert saferepr_unlimited(A()).startswith( "<[ValueError(42) raised in repr()] A object at 0x" ) - - -class TestSafereprUnbounded: - class Help: - def bound_method(self): # pragma: no cover - pass - - def test_saferepr_bound_method(self): - """saferepr() of a bound method should show only the method name""" - assert saferepr(self.Help().bound_method) == "bound_method" - - def test_saferepr_unbounded(self): - """saferepr() of an unbound method should still show the full information""" - obj = self.Help() - # using id() to fetch memory address fails on different platforms - pattern = re.compile( - r"", - ) - assert pattern.match(saferepr(obj)) - assert ( - saferepr(self.Help) - == f"" - ) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 185cd5ef2eb..6a27e52cd26 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -18,6 +18,7 @@ from typing import Mapping from unittest import mock import zipfile +import re import _pytest._code from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE @@ -33,6 +34,7 @@ from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester +from _pytest.assertion.rewrite import _saferepr import pytest @@ -2036,7 +2038,9 @@ def test_foo(): assert test_foo_pyc.is_file() # normal file: not touched by pytest, normal cache tag - bar_init_pyc = get_cache_dir(bar_init) / f"__init__.{sys.implementation.cache_tag}.pyc" + bar_init_pyc = ( + get_cache_dir(bar_init) / f"__init__.{sys.implementation.cache_tag}.pyc" + ) assert bar_init_pyc.is_file() @@ -2103,3 +2107,26 @@ def test_foo(): ) result = pytester.runpytest() assert result.ret == 0 + + +class TestSafereprUnbounded: + class Help: + def bound_method(self): # pragma: no cover + pass + + def test_saferepr_bound_method(self): + """saferepr() of a bound method should show only the method name""" + assert _saferepr(self.Help().bound_method) == "bound_method" + + def test_saferepr_unbounded(self): + """saferepr() of an unbound method should still show the full information""" + obj = self.Help() + # using id() to fetch memory address fails on different platforms + pattern = re.compile( + rf"<{Path(__file__).stem}.{self.__class__.__name__}.Help object at 0x[0-9a-fA-F]*>", + ) + assert pattern.match(_saferepr(obj)) + assert ( + _saferepr(self.Help) + == f"" + ) From 7a36eadb265ee97b0259efc6c52a20ddd7113374 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:33:44 +0000 Subject: [PATCH 14/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/_io/saferepr.py | 1 - src/_pytest/assertion/rewrite.py | 26 +++++++------------------- testing/io/test_saferepr.py | 2 -- testing/test_assertrewrite.py | 4 ++-- 4 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 49769913783..13b793f0a77 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -2,7 +2,6 @@ import pprint import reprlib -from types import MethodType def _try_repr_or_str(obj: object) -> str: diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index c3eeca87c0a..bfcbcbd3f8d 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -420,7 +420,7 @@ def _saferepr(obj: object) -> str: if isinstance(obj, types.MethodType): # for bound methods, skip redundant information return obj.__name__ - + maxsize = _get_maxsize_for_saferepr(util._config) return saferepr(obj, maxsize=maxsize).replace("\n", "\\n") @@ -1018,9 +1018,7 @@ def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: ] ): pytest_temp = self.variable() - self.variables_overwrite[self.scope][ - v.left.target.id - ] = v.left # type:ignore[assignment] + self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] v.left.target.id = pytest_temp self.push_format_context() res, expl = self.visit(v) @@ -1064,9 +1062,7 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( self.scope, {} ): - arg = self.variables_overwrite[self.scope][ - arg.id - ] # type:ignore[assignment] + arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] res, expl = self.visit(arg) arg_expls.append(expl) new_args.append(res) @@ -1074,9 +1070,7 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance( keyword.value, ast.Name ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][ - keyword.value.id - ] # type:ignore[assignment] + keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: @@ -1113,13 +1107,9 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: if isinstance( comp.left, ast.Name ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][ - comp.left.id - ] # type:ignore[assignment] + comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][ - comp.left.target.id - ] = comp.left # type:ignore[assignment] + self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): left_expl = f"({left_expl})" @@ -1137,9 +1127,7 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: and next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][ - left_res.id - ] = next_operand # type:ignore[assignment] + self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): next_expl = f"({next_expl})" diff --git a/testing/io/test_saferepr.py b/testing/io/test_saferepr.py index 7a3066ac6a5..075d40cdf44 100644 --- a/testing/io/test_saferepr.py +++ b/testing/io/test_saferepr.py @@ -1,8 +1,6 @@ # mypy: allow-untyped-defs from __future__ import annotations -import re - from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 6a27e52cd26..5ee40ee6568 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -10,6 +10,7 @@ import os from pathlib import Path import py_compile +import re import stat import sys import textwrap @@ -18,13 +19,13 @@ from typing import Mapping from unittest import mock import zipfile -import re import _pytest._code from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE from _pytest.assertion import util from _pytest.assertion.rewrite import _get_assertion_exprs from _pytest.assertion.rewrite import _get_maxsize_for_saferepr +from _pytest.assertion.rewrite import _saferepr from _pytest.assertion.rewrite import AssertionRewritingHook from _pytest.assertion.rewrite import get_cache_dir from _pytest.assertion.rewrite import PYC_TAIL @@ -34,7 +35,6 @@ from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester -from _pytest.assertion.rewrite import _saferepr import pytest From d7ca96cf281cacf19a8d4229830d29fc8d469b95 Mon Sep 17 00:00:00 2001 From: Farbod Ahmadian Date: Fri, 21 Jun 2024 17:32:46 +0200 Subject: [PATCH 15/16] refactor: move bound_method repr from global version to rewrite module --- src/_pytest/_io/saferepr.py | 1 + src/_pytest/assertion/rewrite.py | 24 ++++++++++++++++++------ testing/test_assertrewrite.py | 2 ++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 13b793f0a77..207d402411d 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -59,6 +59,7 @@ def repr(self, x: object) -> str: if self.use_ascii: s = ascii(x) else: + s = super().repr(x) except (KeyboardInterrupt, SystemExit): raise diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index bfcbcbd3f8d..33e056af978 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -1018,7 +1018,9 @@ def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: ] ): pytest_temp = self.variable() - self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] + self.variables_overwrite[self.scope][ + v.left.target.id + ] = v.left # type:ignore[assignment] v.left.target.id = pytest_temp self.push_format_context() res, expl = self.visit(v) @@ -1062,7 +1064,9 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( self.scope, {} ): - arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] + arg = self.variables_overwrite[self.scope][ + arg.id + ] # type:ignore[assignment] res, expl = self.visit(arg) arg_expls.append(expl) new_args.append(res) @@ -1070,7 +1074,9 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance( keyword.value, ast.Name ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] + keyword.value = self.variables_overwrite[self.scope][ + keyword.value.id + ] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: @@ -1107,9 +1113,13 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: if isinstance( comp.left, ast.Name ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] + comp.left = self.variables_overwrite[self.scope][ + comp.left.id + ] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] + self.variables_overwrite[self.scope][ + comp.left.target.id + ] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): left_expl = f"({left_expl})" @@ -1127,7 +1137,9 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: and next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] + self.variables_overwrite[self.scope][ + left_res.id + ] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): next_expl = f"({next_expl})" diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 5ee40ee6568..9047fe2a836 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -19,6 +19,7 @@ from typing import Mapping from unittest import mock import zipfile +import re import _pytest._code from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE @@ -35,6 +36,7 @@ from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester +from _pytest.assertion.rewrite import _saferepr import pytest From f6fef46265fe786d0be72c2307fcf4260f8860db Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2024 15:46:09 +0000 Subject: [PATCH 16/16] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/_pytest/_io/saferepr.py | 1 - src/_pytest/assertion/rewrite.py | 24 ++++++------------------ testing/test_assertrewrite.py | 2 -- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/src/_pytest/_io/saferepr.py b/src/_pytest/_io/saferepr.py index 207d402411d..13b793f0a77 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -59,7 +59,6 @@ def repr(self, x: object) -> str: if self.use_ascii: s = ascii(x) else: - s = super().repr(x) except (KeyboardInterrupt, SystemExit): raise diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 33e056af978..bfcbcbd3f8d 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -1018,9 +1018,7 @@ def visit_BoolOp(self, boolop: ast.BoolOp) -> tuple[ast.Name, str]: ] ): pytest_temp = self.variable() - self.variables_overwrite[self.scope][ - v.left.target.id - ] = v.left # type:ignore[assignment] + self.variables_overwrite[self.scope][v.left.target.id] = v.left # type:ignore[assignment] v.left.target.id = pytest_temp self.push_format_context() res, expl = self.visit(v) @@ -1064,9 +1062,7 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance(arg, ast.Name) and arg.id in self.variables_overwrite.get( self.scope, {} ): - arg = self.variables_overwrite[self.scope][ - arg.id - ] # type:ignore[assignment] + arg = self.variables_overwrite[self.scope][arg.id] # type:ignore[assignment] res, expl = self.visit(arg) arg_expls.append(expl) new_args.append(res) @@ -1074,9 +1070,7 @@ def visit_Call(self, call: ast.Call) -> tuple[ast.Name, str]: if isinstance( keyword.value, ast.Name ) and keyword.value.id in self.variables_overwrite.get(self.scope, {}): - keyword.value = self.variables_overwrite[self.scope][ - keyword.value.id - ] # type:ignore[assignment] + keyword.value = self.variables_overwrite[self.scope][keyword.value.id] # type:ignore[assignment] res, expl = self.visit(keyword.value) new_kwargs.append(ast.keyword(keyword.arg, res)) if keyword.arg: @@ -1113,13 +1107,9 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: if isinstance( comp.left, ast.Name ) and comp.left.id in self.variables_overwrite.get(self.scope, {}): - comp.left = self.variables_overwrite[self.scope][ - comp.left.id - ] # type:ignore[assignment] + comp.left = self.variables_overwrite[self.scope][comp.left.id] # type:ignore[assignment] if isinstance(comp.left, ast.NamedExpr): - self.variables_overwrite[self.scope][ - comp.left.target.id - ] = comp.left # type:ignore[assignment] + self.variables_overwrite[self.scope][comp.left.target.id] = comp.left # type:ignore[assignment] left_res, left_expl = self.visit(comp.left) if isinstance(comp.left, (ast.Compare, ast.BoolOp)): left_expl = f"({left_expl})" @@ -1137,9 +1127,7 @@ def visit_Compare(self, comp: ast.Compare) -> tuple[ast.expr, str]: and next_operand.target.id == left_res.id ): next_operand.target.id = self.variable() - self.variables_overwrite[self.scope][ - left_res.id - ] = next_operand # type:ignore[assignment] + self.variables_overwrite[self.scope][left_res.id] = next_operand # type:ignore[assignment] next_res, next_expl = self.visit(next_operand) if isinstance(next_operand, (ast.Compare, ast.BoolOp)): next_expl = f"({next_expl})" diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index 9047fe2a836..5ee40ee6568 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -19,7 +19,6 @@ from typing import Mapping from unittest import mock import zipfile -import re import _pytest._code from _pytest._io.saferepr import DEFAULT_REPR_MAX_SIZE @@ -36,7 +35,6 @@ from _pytest.config import ExitCode from _pytest.pathlib import make_numbered_dir from _pytest.pytester import Pytester -from _pytest.assertion.rewrite import _saferepr import pytest