From 9543d1901f3704432ff2f7a21d4a17fe9cf6cb2c Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Tue, 22 Jan 2019 19:42:22 -0200 Subject: [PATCH] Group warnings by message instead of by test id --- changelog/4402.bugfix.rst | 4 ++ src/_pytest/terminal.py | 42 +++++++++---------- .../test_group_warnings_by_message.py | 16 +++++++ testing/test_warnings.py | 19 +++++++++ 4 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 changelog/4402.bugfix.rst create mode 100644 testing/example_scripts/warnings/test_group_warnings_by_message.py diff --git a/changelog/4402.bugfix.rst b/changelog/4402.bugfix.rst new file mode 100644 index 000000000..9b338aaa5 --- /dev/null +++ b/changelog/4402.bugfix.rst @@ -0,0 +1,4 @@ +Warning summary now groups warnings by message instead of by test id. + +This makes the output more compact and better conveys the general idea of how much code is +actually generating warnings, instead of how many tests call that code. diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index bea02306b..2a3d71a69 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -7,7 +7,7 @@ from __future__ import division from __future__ import print_function import argparse -import itertools +import collections import platform import sys import time @@ -724,33 +724,33 @@ class TerminalReporter(object): final = hasattr(self, "_already_displayed_warnings") if final: - warnings = all_warnings[self._already_displayed_warnings :] + warning_reports = all_warnings[self._already_displayed_warnings :] else: - warnings = all_warnings - self._already_displayed_warnings = len(warnings) - if not warnings: + warning_reports = all_warnings + self._already_displayed_warnings = len(warning_reports) + if not warning_reports: return - grouped = itertools.groupby( - warnings, key=lambda wr: wr.get_location(self.config) - ) + reports_grouped_by_message = collections.OrderedDict() + for wr in warning_reports: + reports_grouped_by_message.setdefault(wr.message, []).append(wr) title = "warnings summary (final)" if final else "warnings summary" self.write_sep("=", title, yellow=True, bold=False) - for location, warning_records in grouped: - # legacy warnings show their location explicitly, while standard warnings look better without - # it because the location is already formatted into the message - warning_records = list(warning_records) - if location: - self._tw.line(str(location)) - for w in warning_records: + for message, warning_reports in reports_grouped_by_message.items(): + has_any_location = False + for w in warning_reports: + location = w.get_location(self.config) if location: - lines = w.message.splitlines() - indented = "\n".join(" " + x for x in lines) - message = indented.rstrip() - else: - message = w.message.rstrip() - self._tw.line(message) + self._tw.line(str(location)) + has_any_location = True + if has_any_location: + lines = message.splitlines() + indented = "\n".join(" " + x for x in lines) + message = indented.rstrip() + else: + message = message.rstrip() + self._tw.line(message) self._tw.line() self._tw.line("-- Docs: https://docs.pytest.org/en/latest/warnings.html") diff --git a/testing/example_scripts/warnings/test_group_warnings_by_message.py b/testing/example_scripts/warnings/test_group_warnings_by_message.py new file mode 100644 index 000000000..c736135b7 --- /dev/null +++ b/testing/example_scripts/warnings/test_group_warnings_by_message.py @@ -0,0 +1,16 @@ +import warnings + +import pytest + + +def func(): + warnings.warn(UserWarning("foo")) + + +@pytest.mark.parametrize("i", range(5)) +def test_foo(i): + func() + + +def test_bar(): + func() diff --git a/testing/test_warnings.py b/testing/test_warnings.py index e8075b617..984aae027 100644 --- a/testing/test_warnings.py +++ b/testing/test_warnings.py @@ -693,3 +693,22 @@ def test_warnings_checker_twice(): warnings.warn("Message A", UserWarning) with expectation: warnings.warn("Message B", UserWarning) + + +@pytest.mark.filterwarnings("always") +def test_group_warnings_by_message(testdir): + testdir.copy_example("warnings/test_group_warnings_by_message.py") + result = testdir.runpytest() + result.stdout.fnmatch_lines( + [ + "test_group_warnings_by_message.py::test_foo[0]", + "test_group_warnings_by_message.py::test_foo[1]", + "test_group_warnings_by_message.py::test_foo[2]", + "test_group_warnings_by_message.py::test_foo[3]", + "test_group_warnings_by_message.py::test_foo[4]", + "test_group_warnings_by_message.py::test_bar", + ] + ) + warning_code = 'warnings.warn(UserWarning("foo"))' + assert warning_code in result.stdout.str() + assert result.stdout.str().count(warning_code) == 1