From 0100f61b62411621f8c5f886221bcbbe6f094a16 Mon Sep 17 00:00:00 2001 From: Bruno Oliveira Date: Wed, 29 Aug 2018 17:53:51 -0300 Subject: [PATCH] Start the laywork to capture standard warnings --- src/_pytest/deprecated.py | 5 +---- src/_pytest/nodes.py | 21 +++++++++++++++++---- src/_pytest/python.py | 20 +++++++++++++------- src/_pytest/terminal.py | 7 +++---- src/_pytest/warning_types.py | 10 ++++++++++ testing/test_mark.py | 2 +- 6 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 src/_pytest/warning_types.py diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index 20f1cc25b..237991c56 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -7,10 +7,7 @@ be removed when the time comes. """ from __future__ import absolute_import, division, print_function - -class RemovedInPytest4Warning(DeprecationWarning): - """warning class for features removed in pytest 4.0""" - +from _pytest.warning_types import RemovedInPytest4Warning MAIN_STR_ARGS = "passing a string to pytest.main() is deprecated, " "pass a list of arguments instead." diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index c8b0f64b7..098136df0 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -1,5 +1,6 @@ from __future__ import absolute_import, division, print_function import os +import warnings import six import py @@ -7,6 +8,7 @@ import attr import _pytest import _pytest._code +from _pytest.compat import getfslineno from _pytest.mark.structures import NodeKeywords, MarkInfo @@ -145,6 +147,14 @@ class Node(object): ) ) + def std_warn(self, message, category=None): + from _pytest.warning_types import PytestWarning + + if category is None: + assert isinstance(message, PytestWarning) + path, lineno = get_fslocation_from_item(self) + warnings.warn_explicit(message, category, filename=str(path), lineno=lineno) + # methods for ordering nodes @property def nodeid(self): @@ -314,10 +324,13 @@ def get_fslocation_from_item(item): * "fslocation": a pair (path, lineno) * "fspath": just a path """ - fslocation = getattr(item, "location", None) - if fslocation is None: - fslocation = getattr(item, "fspath", None) - return fslocation + result = getattr(item, "location", None) + if result is not None: + return result + obj = getattr(item, "obj", None) + if obj is not None: + return getfslineno(obj) + return getattr(item, "fspath", None), None class Collector(Node): diff --git a/src/_pytest/python.py b/src/_pytest/python.py index a4c3c3252..9de0dc0ec 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -44,7 +44,7 @@ from _pytest.mark.structures import ( get_unpacked_marks, normalize_mark_list, ) - +from _pytest.warning_types import PytestUsageWarning # relative paths that we use to filter traceback entries from appearing to the user; # see filter_traceback @@ -656,17 +656,23 @@ class Class(PyCollector): if not safe_getattr(self.obj, "__test__", True): return [] if hasinit(self.obj): - self.warn( - "C1", + # self.warn( + # "C1", + # "cannot collect test class %r because it has a " + # "__init__ constructor" % self.obj.__name__, + # ) + self.std_warn( "cannot collect test class %r because it has a " "__init__ constructor" % self.obj.__name__, + PytestUsageWarning, ) return [] elif hasnew(self.obj): - self.warn( - "C1", - "cannot collect test class %r because it has a " - "__new__ constructor" % self.obj.__name__, + self.std_warn( + PytestUsageWarning( + "cannot collect test class %r because it has a " + "__new__ constructor" % self.obj.__name__ + ) ) return [] return [self._getcustomclass("Instance")(name="()", parent=self)] diff --git a/src/_pytest/terminal.py b/src/_pytest/terminal.py index 41b0e755c..5140741a3 100644 --- a/src/_pytest/terminal.py +++ b/src/_pytest/terminal.py @@ -331,12 +331,11 @@ class TerminalReporter(object): warnings.append(warning) def pytest_warning_captured(self, warning_message, item): - from _pytest.nodes import get_fslocation_from_item + # from _pytest.nodes import get_fslocation_from_item from _pytest.warnings import warning_record_to_str warnings = self.stats.setdefault("warnings", []) - - fslocation = get_fslocation_from_item(item) + fslocation = warning_message.filename, warning_message.lineno message = warning_record_to_str(warning_message) nodeid = item.nodeid if item is not None else "" @@ -713,7 +712,7 @@ class TerminalReporter(object): for w in warning_records: lines = w.message.splitlines() indented = "\n".join(" " + x for x in lines) - self._tw.line(indented) + self._tw.line(indented.rstrip()) self._tw.line() self._tw.line("-- Docs: https://docs.pytest.org/en/latest/warnings.html") diff --git a/src/_pytest/warning_types.py b/src/_pytest/warning_types.py new file mode 100644 index 000000000..2b86dd289 --- /dev/null +++ b/src/_pytest/warning_types.py @@ -0,0 +1,10 @@ +class PytestWarning(UserWarning): + """Base class for all warnings emitted by pytest""" + + +class PytestUsageWarning(PytestWarning): + """Warnings related to pytest usage: either command line or testing code.""" + + +class RemovedInPytest4Warning(PytestWarning): + """warning class for features that will be removed in pytest 4.0""" diff --git a/testing/test_mark.py b/testing/test_mark.py index e47981aca..ae41fb1b8 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -16,7 +16,7 @@ from _pytest.mark import ( from _pytest.nodes import Node ignore_markinfo = pytest.mark.filterwarnings( - "ignore:MarkInfo objects:_pytest.deprecated.RemovedInPytest4Warning" + "ignore:MarkInfo objects:_pytest.warning_types.RemovedInPytest4Warning" )