Skip to content

type-safe markersΒ #11602

Open
Open
@DetachHead

Description

@DetachHead

What's the problem this feature will solve?

currently when creating a custom marker, there's no way for the type checker to ensure the arguments are correct. the builtin markers work around this by having type-checking only subtypes of MarkDecorator like so (in _pytest/mark./structures.py):

# Typing for builtin pytest marks. This is cheating; it gives builtin marks
# special privilege, and breaks modularity. But practicality beats purity...
if TYPE_CHECKING:
    from _pytest.scope import _ScopeName

    class _SkipMarkDecorator(MarkDecorator):
        @overload  # type: ignore[override,misc,no-overload-impl]
        def __call__(self, arg: Markable) -> Markable:
            ...

        @overload
        def __call__(self, reason: str = ...) -> "MarkDecorator":
            ...

Describe the solution you'd like

a way to define markers in a type-safe way, similar to how the builtin markers are defined. maybe something like this:

# conftest.py
class Foo(MarkDecorator):
    @override
    def __call__(self, value: int) -> MarkDecorator: ...
# test_foo.py
from conftest import Foo

@Foo("asdf")  # error: expected int, got str
def test_asdf(): ...

Alternative Solutions

remove the @final from MarkGenerator and allow plugins to subtype it with their custom markers:

# conftest.py
from pytest import MarkGenerator

class CustomMarkers(MarkGenerator):
    foo: Callable[[int], MarkDecorator]
# test_foo.py
from conftest import CustomMarkers

mark = CustomMarkers()

@mark.foo("asdf")  # error: expected int, got str
def test_asdf(): ...

this solution probably isn't the best though since there'd be no way to enforce that users use your subtype. whereas making each marker separate classes would at least allow you to enforce that @Foo is used over @mark.foo using --strict-markers

Metadata

Metadata

Assignees

No one assigned

    Labels

    topic: marksrelated to marks, either the general marks or builtintopic: typingtype-annotation issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions