diff --git a/changelog/2452.removal.rst b/changelog/2452.removal.rst index 2a2e3f810..c2a028303 100644 --- a/changelog/2452.removal.rst +++ b/changelog/2452.removal.rst @@ -1,5 +1,12 @@ -The functions ``Node.warn`` and ``Config.warn`` have been deprecated. Instead of ``Node.warn`` users should now use -``Node.std_warn``, while ``Config.warn`` should be replaced by the standard ``warnings.warn``. +``Config.warn`` has been deprecated, it should be replaced by calls to the standard ``warnings.warn``. + +``Node.warn`` now supports two signatures: + +* ``node.warn(PytestWarning("some message"))``: is now the recommended way to call this function. The warning + instance must be a ``PytestWarning`` or subclass instance. + +* ``node.warn("CI", "some message")``: this code/message form is now deprecated and should be converted to + the warning instance form above. ``RemovedInPytest4Warning`` and ``PytestExperimentalApiWarning`` are now part of the public API and should be accessed using ``pytest.RemovedInPytest4Warning`` and ``pytest.PytestExperimentalApiWarning``. diff --git a/src/_pytest/deprecated.py b/src/_pytest/deprecated.py index d7a07503d..5e2c58962 100644 --- a/src/_pytest/deprecated.py +++ b/src/_pytest/deprecated.py @@ -54,7 +54,7 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning( ) NODE_WARN = RemovedInPytest4Warning( - "Node.warn has been deprecated, use Node.std_warn instead" + "Node.warn(code, message) form has been deprecated, use Node.warn(warning_instance) instead." ) RECORD_XML_PROPERTY = RemovedInPytest4Warning( diff --git a/src/_pytest/junitxml.py b/src/_pytest/junitxml.py index e2579860b..7fa49bc28 100644 --- a/src/_pytest/junitxml.py +++ b/src/_pytest/junitxml.py @@ -262,7 +262,7 @@ def record_xml_property(record_property, request): """(Deprecated) use record_property.""" from _pytest import deprecated - request.node.std_warn(deprecated.RECORD_XML_PROPERTY) + request.node.warn(deprecated.RECORD_XML_PROPERTY) return record_property @@ -275,9 +275,7 @@ def record_xml_attribute(request): """ from _pytest.warning_types import PytestWarning - request.node.std_warn( - PytestWarning("record_xml_attribute is an experimental feature") - ) + request.node.warn(PytestWarning("record_xml_attribute is an experimental feature")) xml = getattr(request.config, "_xml", None) if xml is not None: node_reporter = xml.node_reporter(request.node.nodeid) diff --git a/src/_pytest/mark/structures.py b/src/_pytest/mark/structures.py index 52a780ead..8e8937d59 100644 --- a/src/_pytest/mark/structures.py +++ b/src/_pytest/mark/structures.py @@ -95,7 +95,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")): argval = (argval,) if newmarks and belonging_definition is not None: - belonging_definition.std_warn(MARK_PARAMETERSET_UNPACKING) + belonging_definition.warn(MARK_PARAMETERSET_UNPACKING) return cls(argval, marks=newmarks, id=None) diff --git a/src/_pytest/nodes.py b/src/_pytest/nodes.py index 7a2b48ce2..53d22dc22 100644 --- a/src/_pytest/nodes.py +++ b/src/_pytest/nodes.py @@ -136,7 +136,42 @@ class Node(object): def __repr__(self): return "<%s %r>" % (self.__class__.__name__, getattr(self, "name", None)) - def warn(self, code, message): + def warn(self, code_or_warning, message=None): + """Issue a warning for this item. + + Warnings will be displayed after the test session, unless explicitly suppressed. + + This can be called in two forms: + + **Warning instance** + + This was introduced in pytest 3.8 and uses the standard warning mechanism to issue warnings. + + .. code-block:: python + + node.warn(PytestWarning("some message")) + + The warning instance must be a subclass of :class:`pytest.PytestWarning`. + + **code/message (deprecated)** + + This form was used in pytest prior to 3.8 and is considered deprecated. Using this form will emit another + warning about the deprecation: + + .. code-block:: python + + node.warn("CI", "some message") + + :param Union[Warning,str] code_or_warning: warning instance or warning code (legacy). + :param Union[str,None] message: message to display when called in the legacy form. + :return: + """ + if message is None: + self._std_warn(code_or_warning) + else: + self._legacy_warn(code_or_warning, message) + + def _legacy_warn(self, code, message): """ .. deprecated:: 3.8 @@ -146,7 +181,7 @@ class Node(object): """ from _pytest.deprecated import NODE_WARN - self.std_warn(NODE_WARN) + self._std_warn(NODE_WARN) assert isinstance(code, str) fslocation = get_fslocation_from_item(self) @@ -156,7 +191,7 @@ class Node(object): ) ) - def std_warn(self, warning): + def _std_warn(self, warning): """Issue a warning for this item. Warnings will be displayed after the test session, unless explicitly suppressed @@ -175,8 +210,8 @@ class Node(object): ) path, lineno = get_fslocation_from_item(self) warnings.warn_explicit( - six.text_type(warning), - type(warning), + warning, + category=None, filename=str(path), lineno=lineno + 1 if lineno is not None else None, ) diff --git a/src/_pytest/pytester.py b/src/_pytest/pytester.py index 62127651a..a50999172 100644 --- a/src/_pytest/pytester.py +++ b/src/_pytest/pytester.py @@ -126,7 +126,7 @@ class LsofFdLeakChecker(object): error.append(error[0]) error.append("*** function %s:%s: %s " % item.location) error.append("See issue #2366") - item.std_warn(pytest.PytestWarning("\n".join(error))) + item.warn(pytest.PytestWarning("\n".join(error))) # XXX copied from execnet's conftest.py - needs to be merged diff --git a/src/_pytest/python.py b/src/_pytest/python.py index fa14b7a33..10a3b1ec3 100644 --- a/src/_pytest/python.py +++ b/src/_pytest/python.py @@ -659,7 +659,7 @@ class Class(PyCollector): if not safe_getattr(self.obj, "__test__", True): return [] if hasinit(self.obj): - self.std_warn( + self.warn( PytestWarning( "cannot collect test class %r because it has a " "__init__ constructor" % self.obj.__name__ @@ -667,7 +667,7 @@ class Class(PyCollector): ) return [] elif hasnew(self.obj): - self.std_warn( + self.warn( PytestWarning( "cannot collect test class %r because it has a " "__new__ constructor" % self.obj.__name__ @@ -799,7 +799,7 @@ class Generator(FunctionMixin, PyCollector): ) seen[name] = True values.append(self.Function(name, self, args=args, callobj=call)) - self.std_warn(deprecated.YIELD_TESTS) + self.warn(deprecated.YIELD_TESTS) return values def getcallargs(self, obj): @@ -1106,7 +1106,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr): invocation through the ``request.param`` attribute. """ if self.config: - self.definition.std_warn(deprecated.METAFUNC_ADD_CALL) + self.definition.warn(deprecated.METAFUNC_ADD_CALL) assert funcargs is None or isinstance(funcargs, dict) if funcargs is not None: @@ -1170,7 +1170,7 @@ def _idval(val, argname, idx, idfn, item, config=None): ) msg += " {}: {}\n".format(type(e).__name__, e) msg += "This warning will be an error error in pytest-4.0." - item.std_warn(RemovedInPytest4Warning(msg)) + item.warn(RemovedInPytest4Warning(msg)) if s: return ascii_escaped(s) diff --git a/testing/test_config.py b/testing/test_config.py index c0630d688..fac780a05 100644 --- a/testing/test_config.py +++ b/testing/test_config.py @@ -831,7 +831,7 @@ class TestLegacyWarning(object): ===*warnings summary*=== *test_warn_on_test_item_from_request.py::test_hello* *hello* - *test_warn_on_test_item_from_request.py:7:*Node.warn has been deprecated, use Node.std_warn instead* + *test_warn_on_test_item_from_request.py:7:*Node.warn(code, message) form has been deprecated* """ ) diff --git a/testing/test_mark.py b/testing/test_mark.py index 0a6567521..f50902eb1 100644 --- a/testing/test_mark.py +++ b/testing/test_mark.py @@ -1042,7 +1042,7 @@ class TestKeywordSelection(object): @pytest.mark.filterwarnings("ignore") def test_parameterset_extractfrom(argval, expected): class DummyItem: - def std_warn(self, warning): + def warn(self, warning): pass extracted = ParameterSet.extract_from(argval, belonging_definition=DummyItem()) diff --git a/testing/test_nodes.py b/testing/test_nodes.py index d62d7d78a..9219f45e5 100644 --- a/testing/test_nodes.py +++ b/testing/test_nodes.py @@ -29,4 +29,4 @@ def test_std_warn_not_pytestwarning(testdir): """ ) with pytest.raises(ValueError, match=".*instance of PytestWarning.*"): - items[0].std_warn(UserWarning("some warning")) + items[0].warn(UserWarning("some warning"))