commit
fc2ad1dbed
|
@ -0,0 +1,14 @@
|
|||
Introduce new specific warning ``PytestWarning`` subclasses to make it easier to filter warnings based on the class, rather than on the message. The new subclasses are:
|
||||
|
||||
|
||||
* ``PytestAssertRewriteWarning``
|
||||
|
||||
* ``PytestCacheWarning``
|
||||
|
||||
* ``PytestCollectionWarning``
|
||||
|
||||
* ``PytestConfigWarning``
|
||||
|
||||
* ``PytestUnhandledCoroutineWarning``
|
||||
|
||||
* ``PytestUnknownMarkWarning``
|
|
@ -415,8 +415,20 @@ The following warning types ares used by pytest and are part of the public API:
|
|||
|
||||
.. autoclass:: pytest.PytestWarning
|
||||
|
||||
.. autoclass:: pytest.PytestAssertRewriteWarning
|
||||
|
||||
.. autoclass:: pytest.PytestCacheWarning
|
||||
|
||||
.. autoclass:: pytest.PytestCollectionWarning
|
||||
|
||||
.. autoclass:: pytest.PytestConfigWarning
|
||||
|
||||
.. autoclass:: pytest.PytestDeprecationWarning
|
||||
|
||||
.. autoclass:: pytest.RemovedInPytest4Warning
|
||||
|
||||
.. autoclass:: pytest.PytestExperimentalApiWarning
|
||||
|
||||
.. autoclass:: pytest.PytestUnhandledCoroutineWarning
|
||||
|
||||
.. autoclass:: pytest.PytestUnknownMarkWarning
|
||||
|
||||
.. autoclass:: pytest.RemovedInPytest4Warning
|
||||
|
|
|
@ -268,11 +268,13 @@ class AssertionRewritingHook(object):
|
|||
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,
|
||||
)
|
||||
|
@ -819,11 +821,13 @@ class AssertionRewriter(ast.NodeVisitor):
|
|||
|
||||
"""
|
||||
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,
|
||||
|
@ -887,10 +891,10 @@ class AssertionRewriter(ast.NodeVisitor):
|
|||
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},
|
||||
|
|
|
@ -60,10 +60,10 @@ class Cache(object):
|
|||
|
||||
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,
|
||||
)
|
||||
|
|
|
@ -32,7 +32,7 @@ from _pytest.compat import lru_cache
|
|||
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")
|
||||
|
@ -307,7 +307,7 @@ class PytestPluginManager(PluginManager):
|
|||
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("_", "-")
|
||||
|
@ -574,7 +574,7 @@ class PytestPluginManager(PluginManager):
|
|||
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,
|
||||
)
|
||||
|
@ -863,7 +863,7 @@ class Config(object):
|
|||
from _pytest.warnings import _issue_warning_captured
|
||||
|
||||
_issue_warning_captured(
|
||||
PytestWarning(
|
||||
PytestConfigWarning(
|
||||
"could not load initial conftests: {}".format(e.path)
|
||||
),
|
||||
self.hook,
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -12,7 +12,7 @@ from ..compat import MappingMixin
|
|||
from ..compat import NOTSET
|
||||
from _pytest.deprecated import PYTEST_PARAM_UNKNOWN_KWARGS
|
||||
from _pytest.outcomes import fail
|
||||
from _pytest.warning_types import UnknownMarkWarning
|
||||
from _pytest.warning_types import PytestUnknownMarkWarning
|
||||
|
||||
EMPTY_PARAMETERSET_OPTION = "empty_parameter_set_mark"
|
||||
|
||||
|
@ -318,7 +318,7 @@ class MarkGenerator(object):
|
|||
"Unknown pytest.mark.%s - is this a typo? You can register "
|
||||
"custom marks to avoid this warning - for details, see "
|
||||
"https://docs.pytest.org/en/latest/mark.html" % name,
|
||||
UnknownMarkWarning,
|
||||
PytestUnknownMarkWarning,
|
||||
)
|
||||
|
||||
return MarkDecorator(Mark(name, (), {}))
|
||||
|
|
|
@ -45,7 +45,8 @@ from _pytest.mark.structures import normalize_mark_list
|
|||
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):
|
||||
|
@ -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}
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -721,7 +722,7 @@ class Class(PyCollector):
|
|||
return []
|
||||
if hasinit(self.obj):
|
||||
self.warn(
|
||||
PytestWarning(
|
||||
PytestCollectionWarning(
|
||||
"cannot collect test class %r because it has a "
|
||||
"__init__ constructor" % self.obj.__name__
|
||||
)
|
||||
|
@ -729,7 +730,7 @@ class Class(PyCollector):
|
|||
return []
|
||||
elif hasnew(self.obj):
|
||||
self.warn(
|
||||
PytestWarning(
|
||||
PytestCollectionWarning(
|
||||
"cannot collect test class %r because it has a "
|
||||
"__new__ constructor" % self.obj.__name__
|
||||
)
|
||||
|
|
|
@ -9,12 +9,35 @@ class PytestWarning(UserWarning):
|
|||
"""
|
||||
|
||||
|
||||
class UnknownMarkWarning(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 PytestCacheWarning(PytestWarning):
|
||||
"""
|
||||
Bases: :class:`PytestWarning`.
|
||||
|
||||
Warning emitted by the cache plugin in various situations.
|
||||
"""
|
||||
|
||||
|
||||
class PytestConfigWarning(PytestWarning):
|
||||
"""
|
||||
Bases: :class:`PytestWarning`.
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
|
||||
|
@ -26,14 +49,6 @@ class PytestDeprecationWarning(PytestWarning, DeprecationWarning):
|
|||
"""
|
||||
|
||||
|
||||
class RemovedInPytest4Warning(PytestDeprecationWarning):
|
||||
"""
|
||||
Bases: :class:`pytest.PytestDeprecationWarning`.
|
||||
|
||||
Warning class for features scheduled to be removed in pytest 4.0.
|
||||
"""
|
||||
|
||||
|
||||
class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
|
||||
"""
|
||||
Bases: :class:`pytest.PytestWarning`, :class:`FutureWarning`.
|
||||
|
@ -51,6 +66,33 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
|
|||
)
|
||||
|
||||
|
||||
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.
|
||||
|
|
|
@ -35,8 +35,14 @@ from _pytest.python_api import approx
|
|||
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
|
||||
|
||||
|
@ -66,8 +72,14 @@ __all__ = [
|
|||
"Module",
|
||||
"Package",
|
||||
"param",
|
||||
"PytestAssertRewriteWarning",
|
||||
"PytestCacheWarning",
|
||||
"PytestCollectionWarning",
|
||||
"PytestConfigWarning",
|
||||
"PytestDeprecationWarning",
|
||||
"PytestExperimentalApiWarning",
|
||||
"PytestUnhandledCoroutineWarning",
|
||||
"PytestUnknownMarkWarning",
|
||||
"PytestWarning",
|
||||
"raises",
|
||||
"register_assert_rewrite",
|
||||
|
|
|
@ -630,7 +630,7 @@ def test_removed_in_pytest4_warning_as_error(testdir, change_default):
|
|||
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(
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -169,7 +169,7 @@ filterwarnings =
|
|||
# Do not cause SyntaxError for invalid escape sequences in py37.
|
||||
default:invalid escape sequence:DeprecationWarning
|
||||
# ignore use of unregistered marks, because we use many to test the implementation
|
||||
ignore::_pytest.warning_types.UnknownMarkWarning
|
||||
ignore::_pytest.warning_types.PytestUnknownMarkWarning
|
||||
pytester_example_dir = testing/example_scripts
|
||||
markers =
|
||||
issue
|
||||
|
|
Loading…
Reference in New Issue