Skip to content

Node.get_marker needs a proper replacement #3446

Closed
@The-Compiler

Description

@The-Compiler

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions