Skip to content

Commit

Permalink
Introduce new warning subclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
nicoddemus committed Apr 28, 2019
1 parent 8532e99 commit 53cd7fd
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 33 deletions.
14 changes: 12 additions & 2 deletions doc/en/warnings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -415,10 +415,20 @@ The following warning types ares used by pytest and are part of the public API:

.. autoclass:: pytest.PytestWarning

.. autoclass:: pytest.PytestDeprecationWarning
.. autoclass:: pytest.PytestAssertRewriteWarning

.. autoclass:: pytest.RemovedInPytest4Warning
.. autoclass:: pytest.PytestCacheWarning

.. autoclass:: pytest.PytestCollectionWarning

.. autoclass:: pytest.PytestConfigWarning

.. autoclass:: pytest.PytestDeprecationWarning

.. autoclass:: pytest.PytestExperimentalApiWarning

.. autoclass:: pytest.PytestUnhandledCoroutineWarning

.. autoclass:: pytest.PytestUnknownMarkWarning

.. autoclass:: pytest.RemovedInPytest4Warning
16 changes: 10 additions & 6 deletions src/_pytest/assertion/rewrite.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,11 +268,13 @@ def mark_rewrite(self, *names):
self._marked_for_rewrite_cache.clear()

def _warn_already_imported(self, name):
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestAssertRewriteWarning
from _pytest.warnings import _issue_warning_captured

_issue_warning_captured(
PytestWarning("Module already imported so cannot be rewritten: %s" % name),
PytestAssertRewriteWarning(
"Module already imported so cannot be rewritten: %s" % name
),
self.config.hook,
stacklevel=5,
)
Expand Down Expand Up @@ -819,11 +821,13 @@ def visit_Assert(self, assert_):
"""
if isinstance(assert_.test, ast.Tuple) and len(assert_.test.elts) >= 1:
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestAssertRewriteWarning
import warnings

warnings.warn_explicit(
PytestWarning("assertion is always true, perhaps remove parentheses?"),
PytestAssertRewriteWarning(
"assertion is always true, perhaps remove parentheses?"
),
category=None,
filename=str(self.module_path),
lineno=assert_.lineno,
Expand Down Expand Up @@ -887,10 +891,10 @@ def warn_about_none_ast(self, node, module_path, lineno):
val_is_none = ast.Compare(node, [ast.Is()], [AST_NONE])
send_warning = ast.parse(
"""
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestAssertRewriteWarning
from warnings import warn_explicit
warn_explicit(
PytestWarning('asserting the value None, please use "assert is None"'),
PytestAssertRewriteWarning('asserting the value None, please use "assert is None"'),
category=None,
filename={filename!r},
lineno={lineno},
Expand Down
4 changes: 2 additions & 2 deletions src/_pytest/cacheprovider.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def cache_dir_from_config(config):

def warn(self, fmt, **args):
from _pytest.warnings import _issue_warning_captured
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestCacheWarning

_issue_warning_captured(
PytestWarning(fmt.format(**args) if args else fmt),
PytestCacheWarning(fmt.format(**args) if args else fmt),
self._config.hook,
stacklevel=3,
)
Expand Down
8 changes: 4 additions & 4 deletions src/_pytest/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
from _pytest.compat import safe_str
from _pytest.outcomes import fail
from _pytest.outcomes import Skipped
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestConfigWarning

hookimpl = HookimplMarker("pytest")
hookspec = HookspecMarker("pytest")
Expand Down Expand Up @@ -307,7 +307,7 @@ def parse_hookspec_opts(self, module_or_class, name):
def register(self, plugin, name=None):
if name in ["pytest_catchlog", "pytest_capturelog"]:
warnings.warn(
PytestWarning(
PytestConfigWarning(
"{} plugin has been merged into the core, "
"please remove it from your requirements.".format(
name.replace("_", "-")
Expand Down Expand Up @@ -574,7 +574,7 @@ def import_plugin(self, modname, consider_entry_points=False):
from _pytest.warnings import _issue_warning_captured

_issue_warning_captured(
PytestWarning("skipped plugin %r: %s" % (modname, e.msg)),
PytestConfigWarning("skipped plugin %r: %s" % (modname, e.msg)),
self.hook,
stacklevel=1,
)
Expand Down Expand Up @@ -863,7 +863,7 @@ def _preparse(self, args, addopts=True):
from _pytest.warnings import _issue_warning_captured

_issue_warning_captured(
PytestWarning(
PytestConfigWarning(
"could not load initial conftests: {}".format(e.path)
),
self.hook,
Expand Down
6 changes: 4 additions & 2 deletions src/_pytest/junitxml.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,9 +307,11 @@ def record_xml_attribute(request):
The fixture is callable with ``(name, value)``, with value being
automatically xml-encoded
"""
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestExperimentalApiWarning, PytestWarning

request.node.warn(PytestWarning("record_xml_attribute is an experimental feature"))
request.node.warn(
PytestExperimentalApiWarning("record_xml_attribute is an experimental feature")
)

# Declare noop
def add_attr_noop(name, value):
Expand Down
13 changes: 7 additions & 6 deletions src/_pytest/python.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
from _pytest.outcomes import fail
from _pytest.outcomes import skip
from _pytest.pathlib import parts
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning


def pyobj_property(name):
Expand Down Expand Up @@ -171,7 +172,7 @@ def pytest_pyfunc_call(pyfuncitem):
msg += " - pytest-asyncio\n"
msg += " - pytest-trio\n"
msg += " - pytest-tornasync"
warnings.warn(PytestWarning(msg.format(pyfuncitem.nodeid)))
warnings.warn(PytestUnhandledCoroutineWarning(msg.format(pyfuncitem.nodeid)))
skip(msg="coroutine function and no async plugin installed (see warnings)")
funcargs = pyfuncitem.funcargs
testargs = {arg: funcargs[arg] for arg in pyfuncitem._fixtureinfo.argnames}
Expand Down Expand Up @@ -221,7 +222,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
if not (isfunction(obj) or isfunction(get_real_func(obj))):
filename, lineno = getfslineno(obj)
warnings.warn_explicit(
message=PytestWarning(
message=PytestCollectionWarning(
"cannot collect %r because it is not a function." % name
),
category=None,
Expand All @@ -233,7 +234,7 @@ def pytest_pycollect_makeitem(collector, name, obj):
res = Function(name, parent=collector)
reason = deprecated.YIELD_TESTS.format(name=name)
res.add_marker(MARK_GEN.xfail(run=False, reason=reason))
res.warn(PytestWarning(reason))
res.warn(PytestCollectionWarning(reason))
else:
res = list(collector._genfunctions(name, obj))
outcome.force_result(res)
Expand Down Expand Up @@ -721,15 +722,15 @@ def collect(self):
return []
if hasinit(self.obj):
self.warn(
PytestWarning(
PytestCollectionWarning(
"cannot collect test class %r because it has a "
"__init__ constructor" % self.obj.__name__
)
)
return []
elif hasnew(self.obj):
self.warn(
PytestWarning(
PytestCollectionWarning(
"cannot collect test class %r because it has a "
"__new__ constructor" % self.obj.__name__
)
Expand Down
60 changes: 51 additions & 9 deletions src/_pytest/warning_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,43 @@ class PytestWarning(UserWarning):
"""


class PytestUnknownMarkWarning(PytestWarning):
class PytestAssertRewriteWarning(PytestWarning):
"""
Bases: :class:`PytestWarning`.
Warning emitted on use of unknown markers.
See https://docs.pytest.org/en/latest/mark.html for details.
Warning emitted by the pytest assert rewrite module.
"""


class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
class PytestCacheWarning(PytestWarning):
"""
Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`.
Bases: :class:`PytestWarning`.
Warning class for features that will be removed in a future version.
Warning emitted by the cache plugin in various situations.
"""


class RemovedInPytest4Warning(PytestDeprecationWarning):
class PytestConfigWarning(PytestWarning):
"""
Bases: :class:`pytest.PytestDeprecationWarning`.
Bases: :class:`PytestWarning`.
Warning class for features scheduled to be removed in pytest 4.0.
Warning emitted for configuration issues.
"""


class PytestCollectionWarning(PytestWarning):
"""
Bases: :class:`PytestWarning`.
Warning emitted when pytest is not able to collect a file or symbol in a module.
"""


class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
"""
Bases: :class:`pytest.PytestWarning`, :class:`DeprecationWarning`.
Warning class for features that will be removed in a future version.
"""


Expand All @@ -51,6 +66,33 @@ def simple(cls, apiname):
)


class PytestUnhandledCoroutineWarning(PytestWarning):
"""
Bases: :class:`PytestWarning`.
Warning emitted when pytest encounters a test function which is a coroutine,
but it was not handled by any async-aware plugin. Coroutine test functions
are not natively supported.
"""


class PytestUnknownMarkWarning(PytestWarning):
"""
Bases: :class:`PytestWarning`.
Warning emitted on use of unknown markers.
See https://docs.pytest.org/en/latest/mark.html for details.
"""


class RemovedInPytest4Warning(PytestDeprecationWarning):
"""
Bases: :class:`pytest.PytestDeprecationWarning`.
Warning class for features scheduled to be removed in pytest 4.0.
"""


@attr.s
class UnformattedWarning(object):
"""Used to hold warnings that need to format their message at runtime, as opposed to a direct message.
Expand Down
12 changes: 11 additions & 1 deletion src/pytest.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@
from _pytest.python_api import raises
from _pytest.recwarn import deprecated_call
from _pytest.recwarn import warns
from _pytest.warning_types import PytestAssertRewriteWarning
from _pytest.warning_types import PytestCacheWarning
from _pytest.warning_types import PytestCollectionWarning
from _pytest.warning_types import PytestConfigWarning
from _pytest.warning_types import PytestDeprecationWarning
from _pytest.warning_types import PytestExperimentalApiWarning
from _pytest.warning_types import PytestUnhandledCoroutineWarning
from _pytest.warning_types import PytestUnknownMarkWarning
from _pytest.warning_types import PytestWarning
from _pytest.warning_types import RemovedInPytest4Warning
Expand Down Expand Up @@ -67,13 +72,18 @@
"Module",
"Package",
"param",
"PytestAssertRewriteWarning",
"PytestCacheWarning",
"PytestCollectionWarning",
"PytestConfigWarning",
"PytestDeprecationWarning",
"PytestExperimentalApiWarning",
"PytestUnhandledCoroutineWarning",
"PytestUnknownMarkWarning",
"PytestWarning",
"raises",
"register_assert_rewrite",
"RemovedInPytest4Warning",
"PytestUnknownMarkWarning",
"Session",
"set_trace",
"skip",
Expand Down
2 changes: 1 addition & 1 deletion testing/test_warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@ def test():
class TestAssertionWarnings:
@staticmethod
def assert_result_warns(result, msg):
result.stdout.fnmatch_lines(["*PytestWarning: %s*" % msg])
result.stdout.fnmatch_lines(["*PytestAssertRewriteWarning: %s*" % msg])

def test_tuple_warning(self, testdir):
testdir.makepyfile(
Expand Down

0 comments on commit 53cd7fd

Please sign in to comment.