Change std_warn to receive a single warning instance, addressed review suggestions

This commit is contained in:
Bruno Oliveira 2018-09-04 14:20:42 -03:00
parent 284a2d110f
commit b42518acd5
15 changed files with 89 additions and 63 deletions

View File

@ -154,7 +154,7 @@ def get_plugin_manager():
def _prepareconfig(args=None, plugins=None): def _prepareconfig(args=None, plugins=None):
warning_msg = None warning = None
if args is None: if args is None:
args = sys.argv[1:] args = sys.argv[1:]
elif isinstance(args, py.path.local): elif isinstance(args, py.path.local):
@ -165,7 +165,7 @@ def _prepareconfig(args=None, plugins=None):
args = shlex.split(args, posix=sys.platform != "win32") args = shlex.split(args, posix=sys.platform != "win32")
from _pytest import deprecated from _pytest import deprecated
warning_msg = deprecated.MAIN_STR_ARGS warning = deprecated.MAIN_STR_ARGS
config = get_config() config = get_config()
pluginmanager = config.pluginmanager pluginmanager = config.pluginmanager
try: try:
@ -175,11 +175,10 @@ def _prepareconfig(args=None, plugins=None):
pluginmanager.consider_pluginarg(plugin) pluginmanager.consider_pluginarg(plugin)
else: else:
pluginmanager.register(plugin) pluginmanager.register(plugin)
if warning_msg: if warning:
from _pytest.warning_types import PytestWarning
from _pytest.warnings import _issue_config_warning from _pytest.warnings import _issue_config_warning
_issue_config_warning(PytestWarning(warning_msg), config=config) _issue_config_warning(warning, config=config)
return pluginmanager.hook.pytest_cmdline_parse( return pluginmanager.hook.pytest_cmdline_parse(
pluginmanager=pluginmanager, args=args pluginmanager=pluginmanager, args=args
) )

View File

@ -9,9 +9,14 @@ from __future__ import absolute_import, division, print_function
from _pytest.warning_types import RemovedInPytest4Warning from _pytest.warning_types import RemovedInPytest4Warning
MAIN_STR_ARGS = "passing a string to pytest.main() is deprecated, " "pass a list of arguments instead." MAIN_STR_ARGS = RemovedInPytest4Warning(
"passing a string to pytest.main() is deprecated, "
"pass a list of arguments instead."
)
YIELD_TESTS = "yield tests are deprecated, and scheduled to be removed in pytest 4.0" YIELD_TESTS = RemovedInPytest4Warning(
"yield tests are deprecated, and scheduled to be removed in pytest 4.0"
)
FUNCARG_PREFIX = ( FUNCARG_PREFIX = (
'{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated ' '{name}: declaring fixtures using "pytest_funcarg__" prefix is deprecated '
@ -48,7 +53,11 @@ MARK_PARAMETERSET_UNPACKING = RemovedInPytest4Warning(
"For more details, see: https://docs.pytest.org/en/latest/parametrize.html" "For more details, see: https://docs.pytest.org/en/latest/parametrize.html"
) )
RECORD_XML_PROPERTY = ( NODE_WARN = RemovedInPytest4Warning(
"Node.warn has been deprecated, use Node.std_warn instead"
)
RECORD_XML_PROPERTY = RemovedInPytest4Warning(
'Fixture renamed from "record_xml_property" to "record_property" as user ' 'Fixture renamed from "record_xml_property" to "record_property" as user '
"properties are now available to all reporters.\n" "properties are now available to all reporters.\n"
'"record_xml_property" is now deprecated.' '"record_xml_property" is now deprecated.'
@ -58,7 +67,7 @@ COLLECTOR_MAKEITEM = RemovedInPytest4Warning(
"pycollector makeitem was removed " "as it is an accidentially leaked internal api" "pycollector makeitem was removed " "as it is an accidentially leaked internal api"
) )
METAFUNC_ADD_CALL = ( METAFUNC_ADD_CALL = RemovedInPytest4Warning(
"Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.\n" "Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.\n"
"Please use Metafunc.parametrize instead." "Please use Metafunc.parametrize instead."
) )

View File

@ -1257,6 +1257,8 @@ class FixtureManager(object):
items[:] = reorder_items(items) items[:] = reorder_items(items)
def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False): def parsefactories(self, node_or_obj, nodeid=NOTSET, unittest=False):
from _pytest import deprecated
if nodeid is not NOTSET: if nodeid is not NOTSET:
holderobj = node_or_obj holderobj = node_or_obj
else: else:
@ -1279,7 +1281,6 @@ class FixtureManager(object):
if not callable(obj): if not callable(obj):
continue continue
marker = defaultfuncargprefixmarker marker = defaultfuncargprefixmarker
from _pytest import deprecated
filename, lineno = getfslineno(obj) filename, lineno = getfslineno(obj)
warnings.warn_explicit( warnings.warn_explicit(

View File

@ -262,7 +262,7 @@ def record_xml_property(record_property, request):
"""(Deprecated) use record_property.""" """(Deprecated) use record_property."""
from _pytest import deprecated from _pytest import deprecated
request.node.std_warn(deprecated.RECORD_XML_PROPERTY, DeprecationWarning) request.node.std_warn(deprecated.RECORD_XML_PROPERTY)
return record_property return record_property
@ -276,8 +276,7 @@ def record_xml_attribute(request):
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning
request.node.std_warn( request.node.std_warn(
message="record_xml_attribute is an experimental feature", PytestWarning("record_xml_attribute is an experimental feature")
category=PytestWarning,
) )
xml = getattr(request.config, "_xml", None) xml = getattr(request.config, "_xml", None)
if xml is not None: if xml is not None:

View File

@ -65,7 +65,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
return cls(values, marks, id_) return cls(values, marks, id_)
@classmethod @classmethod
def extract_from(cls, parameterset, legacy_force_tuple=False, item=None): def extract_from(cls, parameterset, belonging_definition, legacy_force_tuple=False):
""" """
:param parameterset: :param parameterset:
a legacy style parameterset that may or may not be a tuple, a legacy style parameterset that may or may not be a tuple,
@ -75,7 +75,7 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
enforce tuple wrapping so single argument tuple values enforce tuple wrapping so single argument tuple values
don't get decomposed and break tests don't get decomposed and break tests
:param item: the item that we will be extracting the parameters from. :param belonging_definition: the item that we will be extracting the parameters from.
""" """
if isinstance(parameterset, cls): if isinstance(parameterset, cls):
@ -94,8 +94,8 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
if legacy_force_tuple: if legacy_force_tuple:
argval = (argval,) argval = (argval,)
if newmarks and item is not None: if newmarks and belonging_definition is not None:
item.std_warn(MARK_PARAMETERSET_UNPACKING) belonging_definition.std_warn(MARK_PARAMETERSET_UNPACKING)
return cls(argval, marks=newmarks, id=None) return cls(argval, marks=newmarks, id=None)
@ -108,7 +108,9 @@ class ParameterSet(namedtuple("ParameterSet", "values, marks, id")):
force_tuple = False force_tuple = False
parameters = [ parameters = [
ParameterSet.extract_from( ParameterSet.extract_from(
x, legacy_force_tuple=force_tuple, item=function_definition x,
legacy_force_tuple=force_tuple,
belonging_definition=function_definition,
) )
for x in argvalues for x in argvalues
] ]

View File

@ -144,12 +144,9 @@ class Node(object):
Generate a warning with the given code and message for this item. Generate a warning with the given code and message for this item.
""" """
from _pytest.warning_types import RemovedInPytest4Warning from _pytest.deprecated import NODE_WARN
self.std_warn( self.std_warn(NODE_WARN)
"Node.warn has been deprecated, use Node.std_warn instead",
RemovedInPytest4Warning,
)
assert isinstance(code, str) assert isinstance(code, str)
fslocation = get_fslocation_from_item(self) fslocation = get_fslocation_from_item(self)
@ -159,22 +156,27 @@ class Node(object):
) )
) )
def std_warn(self, message, category=None): def std_warn(self, warning):
"""Issue a warning for this item. """Issue a warning for this item.
Warnings will be displayed after the test session, unless explicitly suppressed Warnings will be displayed after the test session, unless explicitly suppressed
:param Union[str,Warning] message: text message of the warning or ``Warning`` instance. :param Warning warning: the warning instance to issue. Must be a subclass of PytestWarning.
:param Type[Warning] category: warning category.
:raise ValueError: if ``warning`` instance is not a subclass of PytestWarning.
""" """
from _pytest.warning_types import PytestWarning from _pytest.warning_types import PytestWarning
if category is None: if not isinstance(warning, PytestWarning):
assert isinstance(message, PytestWarning) raise ValueError(
"warning must be an instance of PytestWarning or subclass, got {!r}".format(
warning
)
)
path, lineno = get_fslocation_from_item(self) path, lineno = get_fslocation_from_item(self)
warnings.warn_explicit( warnings.warn_explicit(
message, six.text_type(warning),
category, type(warning),
filename=str(path), filename=str(path),
lineno=lineno + 1 if lineno is not None else None, lineno=lineno + 1 if lineno is not None else None,
) )

View File

@ -126,7 +126,7 @@ class LsofFdLeakChecker(object):
error.append(error[0]) error.append(error[0])
error.append("*** function %s:%s: %s " % item.location) error.append("*** function %s:%s: %s " % item.location)
error.append("See issue #2366") error.append("See issue #2366")
item.std_warn("", "\n".join(error), pytest.PytestWarning) item.std_warn(pytest.PytestWarning("\n".join(error)))
# XXX copied from execnet's conftest.py - needs to be merged # XXX copied from execnet's conftest.py - needs to be merged
@ -643,11 +643,9 @@ class Testdir(object):
def copy_example(self, name=None): def copy_example(self, name=None):
import warnings import warnings
from _pytest.warning_types import PYTESTER_COPY_EXAMPLE
warnings.warn( warnings.warn(PYTESTER_COPY_EXAMPLE, stacklevel=2)
pytest.PytestExperimentalApiWarning.simple("testdir.copy_example"),
stacklevel=2,
)
example_dir = self.request.config.getini("pytester_example_dir") example_dir = self.request.config.getini("pytester_example_dir")
if example_dir is None: if example_dir is None:
raise ValueError("pytester_example_dir is unset, can't copy examples") raise ValueError("pytester_example_dir is unset, can't copy examples")

View File

@ -660,9 +660,10 @@ class Class(PyCollector):
return [] return []
if hasinit(self.obj): if hasinit(self.obj):
self.std_warn( self.std_warn(
"cannot collect test class %r because it has a " PytestWarning(
"__init__ constructor" % self.obj.__name__, "cannot collect test class %r because it has a "
PytestWarning, "__init__ constructor" % self.obj.__name__
)
) )
return [] return []
elif hasnew(self.obj): elif hasnew(self.obj):
@ -798,7 +799,7 @@ class Generator(FunctionMixin, PyCollector):
) )
seen[name] = True seen[name] = True
values.append(self.Function(name, self, args=args, callobj=call)) values.append(self.Function(name, self, args=args, callobj=call))
self.std_warn(deprecated.YIELD_TESTS, RemovedInPytest4Warning) self.std_warn(deprecated.YIELD_TESTS)
return values return values
def getcallargs(self, obj): def getcallargs(self, obj):
@ -1105,9 +1106,7 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
invocation through the ``request.param`` attribute. invocation through the ``request.param`` attribute.
""" """
if self.config: if self.config:
self.definition.std_warn( self.definition.std_warn(deprecated.METAFUNC_ADD_CALL)
deprecated.METAFUNC_ADD_CALL, RemovedInPytest4Warning
)
assert funcargs is None or isinstance(funcargs, dict) assert funcargs is None or isinstance(funcargs, dict)
if funcargs is not None: if funcargs is not None:
@ -1158,22 +1157,20 @@ def _find_parametrized_scope(argnames, arg2fixturedefs, indirect):
return "function" return "function"
def _idval(val, argname, idx, idfn, config=None, item=None): def _idval(val, argname, idx, idfn, item, config=None):
if idfn: if idfn:
s = None s = None
try: try:
s = idfn(val) s = idfn(val)
except Exception as e: except Exception as e:
# See issue https://github.com/pytest-dev/pytest/issues/2169 # See issue https://github.com/pytest-dev/pytest/issues/2169
if item is not None: msg = (
# should really be None only when unit-testing this function! "While trying to determine id of parameter {} at position "
msg = ( "{} the following exception was raised:\n".format(argname, idx)
"While trying to determine id of parameter {} at position " )
"{} the following exception was raised:\n".format(argname, idx) msg += " {}: {}\n".format(type(e).__name__, e)
) msg += "This warning will be an error error in pytest-4.0."
msg += " {}: {}\n".format(type(e).__name__, e) item.std_warn(RemovedInPytest4Warning(msg))
msg += "This warning will be an error error in pytest-4.0."
item.std_warn(msg, RemovedInPytest4Warning)
if s: if s:
return ascii_escaped(s) return ascii_escaped(s)
@ -1202,7 +1199,7 @@ def _idvalset(idx, parameterset, argnames, idfn, ids, config=None, item=None):
return parameterset.id return parameterset.id
if ids is None or (idx >= len(ids) or ids[idx] is None): if ids is None or (idx >= len(ids) or ids[idx] is None):
this_id = [ this_id = [
_idval(val, argname, idx, idfn, config, item) _idval(val, argname, idx, idfn, item=item, config=config)
for val, argname in zip(parameterset.values, argnames) for val, argname in zip(parameterset.values, argnames)
] ]
return "-".join(this_id) return "-".join(this_id)

View File

@ -37,3 +37,6 @@ class PytestExperimentalApiWarning(PytestWarning, FutureWarning):
apiname=apiname apiname=apiname
) )
) )
PYTESTER_COPY_EXAMPLE = PytestExperimentalApiWarning.simple("testdir.copy_example")

View File

@ -129,20 +129,20 @@ def warning_record_to_str(warning_message):
return msg return msg
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_runtest_protocol(item): def pytest_runtest_protocol(item):
with catch_warnings_for_item(config=item.config, ihook=item.ihook, item=item): with catch_warnings_for_item(config=item.config, ihook=item.ihook, item=item):
yield yield
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_collection(session): def pytest_collection(session):
config = session.config config = session.config
with catch_warnings_for_item(config=config, ihook=config.hook, item=None): with catch_warnings_for_item(config=config, ihook=config.hook, item=None):
yield yield
@pytest.hookimpl(hookwrapper=True) @pytest.hookimpl(hookwrapper=True, tryfirst=True)
def pytest_terminal_summary(terminalreporter): def pytest_terminal_summary(terminalreporter):
config = terminalreporter.config config = terminalreporter.config
with catch_warnings_for_item(config=config, ihook=config.hook, item=None): with catch_warnings_for_item(config=config, ihook=config.hook, item=None):

View File

@ -526,7 +526,7 @@ class TestInvocationVariants(object):
assert pytest.main == py.test.cmdline.main assert pytest.main == py.test.cmdline.main
def test_invoke_with_string(self, capsys): def test_invoke_with_string(self, capsys):
retcode = pytest.main(["-h"]) retcode = pytest.main("-h")
assert not retcode assert not retcode
out, err = capsys.readouterr() out, err = capsys.readouterr()
assert "--help" in out assert "--help" in out

View File

@ -289,6 +289,7 @@ def test_call_fixture_function_deprecated():
def test_pycollector_makeitem_is_deprecated(): def test_pycollector_makeitem_is_deprecated():
from _pytest.python import PyCollector from _pytest.python import PyCollector
from _pytest.warning_types import RemovedInPytest4Warning
class PyCollectorMock(PyCollector): class PyCollectorMock(PyCollector):
"""evil hack""" """evil hack"""
@ -301,6 +302,6 @@ def test_pycollector_makeitem_is_deprecated():
self.called = True self.called = True
collector = PyCollectorMock() collector = PyCollectorMock()
with pytest.deprecated_call(): with pytest.warns(RemovedInPytest4Warning):
collector.makeitem("foo", "bar") collector.makeitem("foo", "bar")
assert collector.called assert collector.called

View File

@ -217,7 +217,7 @@ class TestMetafunc(object):
def test_idval_hypothesis(self, value): def test_idval_hypothesis(self, value):
from _pytest.python import _idval from _pytest.python import _idval
escaped = _idval(value, "a", 6, None) escaped = _idval(value, "a", 6, None, item=None)
assert isinstance(escaped, str) assert isinstance(escaped, str)
if PY3: if PY3:
escaped.encode("ascii") escaped.encode("ascii")
@ -244,7 +244,7 @@ class TestMetafunc(object):
), ),
] ]
for val, expected in values: for val, expected in values:
assert _idval(val, "a", 6, None) == expected assert _idval(val, "a", 6, None, item=None) == expected
def test_bytes_idval(self): def test_bytes_idval(self):
"""unittest for the expected behavior to obtain ids for parametrized """unittest for the expected behavior to obtain ids for parametrized
@ -262,7 +262,7 @@ class TestMetafunc(object):
(u"αρά".encode("utf-8"), "\\xce\\xb1\\xcf\\x81\\xce\\xac"), (u"αρά".encode("utf-8"), "\\xce\\xb1\\xcf\\x81\\xce\\xac"),
] ]
for val, expected in values: for val, expected in values:
assert _idval(val, "a", 6, None) == expected assert _idval(val, "a", 6, idfn=None, item=None, config=None) == expected
def test_class_or_function_idval(self): def test_class_or_function_idval(self):
"""unittest for the expected behavior to obtain ids for parametrized """unittest for the expected behavior to obtain ids for parametrized
@ -278,7 +278,7 @@ class TestMetafunc(object):
values = [(TestClass, "TestClass"), (test_function, "test_function")] values = [(TestClass, "TestClass"), (test_function, "test_function")]
for val, expected in values: for val, expected in values:
assert _idval(val, "a", 6, None) == expected assert _idval(val, "a", 6, None, item=None) == expected
@pytest.mark.issue250 @pytest.mark.issue250
def test_idmaker_autoname(self): def test_idmaker_autoname(self):

View File

@ -1041,7 +1041,11 @@ class TestKeywordSelection(object):
) )
@pytest.mark.filterwarnings("ignore") @pytest.mark.filterwarnings("ignore")
def test_parameterset_extractfrom(argval, expected): def test_parameterset_extractfrom(argval, expected):
extracted = ParameterSet.extract_from(argval) class DummyItem:
def std_warn(self, warning):
pass
extracted = ParameterSet.extract_from(argval, belonging_definition=DummyItem())
assert extracted == expected assert extracted == expected

View File

@ -19,3 +19,14 @@ from _pytest import nodes
def test_ischildnode(baseid, nodeid, expected): def test_ischildnode(baseid, nodeid, expected):
result = nodes.ischildnode(baseid, nodeid) result = nodes.ischildnode(baseid, nodeid)
assert result is expected assert result is expected
def test_std_warn_not_pytestwarning(testdir):
items = testdir.getitems(
"""
def test():
pass
"""
)
with pytest.raises(ValueError, match=".*instance of PytestWarning.*"):
items[0].std_warn(UserWarning("some warning"))