Description
Continuing the discussion from #3317 (especially #3317 (comment)) here - cc @nicoddemus @RonnyPfannschmidt @flub
Current situation
I'm trying the 3.6.0 release (#3445), here's what I see with -Wall
:
pytest-rerunfailures
breaks
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR> [...]
INTERNALERROR> File ".../lib/python3.6/site-packages/pytest_rerunfailures.py", line 123, in pytest_runtest_protocol
INTERNALERROR> reruns = get_reruns_count(item)
INTERNALERROR> File ".../lib/python3.6/site-packages/pytest_rerunfailures.py", line 80, in get_reruns_count
INTERNALERROR> if "reruns" in rerun_marker.kwargs:
INTERNALERROR> File ".../lib/python3.6/site-packages/_pytest/mark/structures.py", line 20, in warned
INTERNALERROR> warnings.warn(warning, stacklevel=2)
INTERNALERROR> _pytest.deprecated.RemovedInPytest4Warning: MarkInfo objects are deprecated as they contain the merged marks.
INTERNALERROR> Please use node.iter_markers to iterate over markers correctly
pytest-qt
breaks
def pytest_runtest_setup(self, item):
if item.get_marker('no_qt_log'):
return
m = item.get_marker('qt_log_ignore')
if m:
> if not set(m.kwargs).issubset(set(['extend'])):
local things in my testsuite break
xfail = request.node.get_marker('xfail')
if xfail and (not xfail.args or xfail.args[0]):
...
@pytest.fixture(autouse=True)
def apply_fake_os(monkeypatch, request):
fake_os = request.node.get_marker('fake_os')
if not fake_os:
return
> name = fake_os.args[0]
E _pytest.deprecated.RemovedInPytest4Warning: MarkInfo objects are deprecated as they contain the merged marks.
E Please use node.iter_markers to iterate over markers correctly
Problems
Insufficient documentation
So, the deprecation warning tells me to use node.iter_markers
. Looking at the docs, all I can see is "iterate over all markers of the node" and for get_marker(name)
a "warning: deprecated" (with wrong ReST-syntax, too).
If I'm not aware of the problem of marker smearing (and I wasn't really, I've never done strange subclassing stuff in tests), I'm quite lost there. If it wasn't for @nicoddemus' example I would be pretty lost here, and write something based on guesswork.
Bad abstraction
The suggested alternative to
fake_os = request.node.get_marker('fake_os')
if not fake_os:
return
name = fake_os.args[0]
...
Is supposedly:
for fake_os in request.node.iter_markers():
if fake_os.name == 'fake_os':
break
else:
return
name = fake_os.args[0]
...
With that kind of ugly construct, I can guarantee you: People will make mistakes (or plugins will handle it differently) and you'll end up with more issues regarding marker handling than before. Suddenly, only some markers will smear, others will have other funny issues (what if I forget the else:
for the for
), and so on.
We want the default case to be that only the closest marker applies, right? So get an equivalent API which does that, and document that's the replacement for the "default case" where you don't need to worry about those issues at all.
Conclusion
I feel like this should block a 3.6.0 release. If we release something with deprecation warnings, no replacement, and bad documentation, then plugins and testsuites which want to preserve backwards compatibility will end up with three ways of getting a marker, each probably different in another subtle way.