From 4496e36e70f771ea4cbe647f4426728f9aca871b Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 13:31:42 +0200 Subject: [PATCH 1/5] WIP: assert: use safeformat for display with -vv TODO: - [ ] real plugin hook? - [ ] should handle other places where safeformat is used also, via args/options to the hook then likely. Ref: https://github.com/pytest-dev/pytest/issues/3962 Ref: https://github.com/pytest-dev/pytest/pull/5933 --- src/_pytest/assertion/__init__.py | 12 ++++++++++++ src/_pytest/assertion/rewrite.py | 7 +++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index 3b42b356d5b..2d54c912fce 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -141,6 +141,17 @@ def callbinrepr(op, left, right): util._reprcompare = callbinrepr + from _pytest._io.saferepr import safeformat + from _pytest._io.saferepr import saferepr + + def call_assertion_display_hook(obj): + verbose = item.config.getoption("verbose") + if verbose > 1: + return safeformat(obj) + return saferepr(obj).replace("\n", "\\n") + + util._reprdisplay = call_assertion_display_hook + if item.ihook.pytest_assertion_pass.get_hookimpls(): def call_assertion_pass_hook(lineno, orig, expl): @@ -153,6 +164,7 @@ def call_assertion_pass_hook(lineno, orig, expl): def pytest_runtest_teardown(item): util._reprcompare = None + util._reprdisplay = None util._assertion_pass = None diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index c225eff5fb5..34ba8b0a2d6 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -398,6 +398,10 @@ def _call_reprcompare(ops, results, expls, each_obj): return expl +def _call_reprdisplay(obj): + return util._reprdisplay(obj) + + def _call_assertion_pass(lineno, orig, expl): # type: (int, str, str) -> None if util._assertion_pass is not None: @@ -662,8 +666,7 @@ def assign(self, expr): return ast.Name(name, ast.Load()) def display(self, expr): - """Call saferepr on the expression.""" - return self.helper("_saferepr", expr) + return self.helper("_call_reprdisplay", expr) def helper(self, name, *args): """Call a helper in this module.""" From 0d9d3bad96807a0b997cf6203f61799ba8ae2075 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 14:08:40 +0200 Subject: [PATCH 2/5] keep newlines --- src/_pytest/assertion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index 2d54c912fce..a7c783dd36e 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -147,7 +147,7 @@ def callbinrepr(op, left, right): def call_assertion_display_hook(obj): verbose = item.config.getoption("verbose") if verbose > 1: - return safeformat(obj) + return safeformat(obj).replace("\n", "\n~") return saferepr(obj).replace("\n", "\\n") util._reprdisplay = call_assertion_display_hook From 4cde6084b01a61dea14b097fe3178afe6ee611b2 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 14:12:09 +0200 Subject: [PATCH 3/5] _call_reprdisplay: fallback for no util._reprdisplay for unknown reasons --- src/_pytest/assertion/rewrite.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/_pytest/assertion/rewrite.py b/src/_pytest/assertion/rewrite.py index 34ba8b0a2d6..92194f677cc 100644 --- a/src/_pytest/assertion/rewrite.py +++ b/src/_pytest/assertion/rewrite.py @@ -399,7 +399,10 @@ def _call_reprcompare(ops, results, expls, each_obj): def _call_reprdisplay(obj): - return util._reprdisplay(obj) + if util._reprdisplay is not None: + return util._reprdisplay(obj) + # XXX: happens/used with testing/test_assertion.py::TestImportHookInstallation::test_rewrite_assertions_pytester_plugin # noqa: E501 + return _saferepr(obj) def _call_assertion_pass(lineno, orig, expl): From a78ec1f0aed1f423d0641edb98f6b45cc6fd91ee Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 22:42:18 +0200 Subject: [PATCH 4/5] safeformat: allow passing through kwargs --- 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 7704421a22e..6e4207cbb0b 100644 --- a/src/_pytest/_io/saferepr.py +++ b/src/_pytest/_io/saferepr.py @@ -46,13 +46,13 @@ def repr_instance(self, x, level): return _ellipsize(s, self.maxsize) -def safeformat(obj): +def safeformat(obj, **kwargs): """return a pretty printed string for the given object. Failing __repr__ functions of user instances will be represented with a short exception info. """ try: - return pprint.pformat(obj) + return pprint.pformat(obj, **kwargs) except Exception as exc: return _format_repr_exception(exc, obj) From c4abaf4216096049621162f1fb49a6545b1a1508 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 21 Oct 2019 22:42:33 +0200 Subject: [PATCH 5/5] call_assertion_display_hook: use compact=True with safeformat --- src/_pytest/assertion/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_pytest/assertion/__init__.py b/src/_pytest/assertion/__init__.py index a7c783dd36e..5c4405f1ff3 100644 --- a/src/_pytest/assertion/__init__.py +++ b/src/_pytest/assertion/__init__.py @@ -147,7 +147,7 @@ def callbinrepr(op, left, right): def call_assertion_display_hook(obj): verbose = item.config.getoption("verbose") if verbose > 1: - return safeformat(obj).replace("\n", "\n~") + return safeformat(obj, compact=True).replace("\n", "\n~") return saferepr(obj).replace("\n", "\\n") util._reprdisplay = call_assertion_display_hook