Skip to content

Better handling of mark signatures #5418

Closed
@nicoddemus

Description

@nicoddemus

A known problem when dealing with marks is to properly support the intended signature, as this can be tricky and error prone.

For example, the official signature of skipif is:

skipif(condition, *, reason=None)

But the skipping plugin needs to manually handle this signature using args and kwargs, which is error prone and has been a source of errors in the past (letting an unknown keyword argument slip through silently for example).

Someone mentioned about using actual Python functions to declare mark signatures in our pytest sprint back in 2016 (I think it was @RonnyPfannschmidt or @hpk42). I decided to play a bit with it and got excited about how simple it was to get this working (thanks to @RonnyPfannschmidt's large mark refactoring):

import pytest
from _pytest.mark import MarkDecorator, Mark


def skipif(condition, *, reason=None):
    return MarkDecorator(Mark('skipif', (condition,), dict(reason=reason)))


def parametrize(argnames, argvalues, *, indirect=False, ids=None, scope=None):
    return MarkDecorator(
        Mark('parametrize', (argnames, argvalues), dict(indirect=indirect, ids=ids, scope=scope)))


pytest.mark.skipif = skipif
pytest.mark.parametrize = parametrize


@pytest.mark.skipif(False, reason='some reason')
def test(request):
    print(request.node.get_closest_marker('skipif'))


@pytest.mark.parametrize('x', range(10), ids=None)
def test_foo(x):
    print(x)

If we pass the wrong arguments, we get the expected error:

@pytest.mark.skipif(foo=3)
def test_bar(request):
    pass
    @pytest.mark.skipif(foo=3)
E   TypeError: skipif() got an unexpected keyword argument 'foo'

And even better, at collection time instead of at runtime.


While we should have a large discussion on the design of an API to expose this functionality to users for custom marks, I propose we adopt this strategy in 5.0 for the builtin marks. This will help users catch errors now, while not exposing any implementation detail. Even if we decide on a completely different approach in the future, this is transparent to users.

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: marksrelated to marks, either the general marks or builtintype: proposalproposal for a new feature, often to gather opinions or design the API around the new feature

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions