Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mypy plugin for @mark.parametrize #9334

Open
Jasha10 opened this issue Nov 23, 2021 · 2 comments
Open

mypy plugin for @mark.parametrize #9334

Jasha10 opened this issue Nov 23, 2021 · 2 comments
Labels
type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature

Comments

@Jasha10
Copy link

Jasha10 commented Nov 23, 2021

The idea is that a plugin would allow the arguments to the @pytest.mark.parametrize decorator to be type-checked.

For example:

import pytest

@pytest.mark.parametrize(
    "arg",
    [
        123,        # correct type int
        456.789,    # wrong type float, mypy plugin to complain
    ]
)
def test_foo(
    arg: int   # plugin reads `int` annotation here
) -> None:
    ...
@Jasha10 Jasha10 changed the title mypy plugin for @mark.parametrizev mypy plugin for @mark.parametrize Nov 23, 2021
@Jasha10 Jasha10 changed the title mypy plugin for @mark.parametrize mypy plugin for @mark.parametrize Nov 23, 2021
@The-Compiler
Copy link
Member

That seems like a good idea - but I don't think it should be part of pytest core.

@Zac-HD Zac-HD added the type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature label Nov 24, 2021
@tirkarthi
Copy link
Contributor

Nice idea. I found that there is typeguard project which can be used to perform run-time type checks.

# conftest.py
import typeguard


def pytest_collection_modifyitems(session, config, items):
    for item in items:
        item.obj = typeguard.typechecked(item.obj)
pytest -vvv          
==================================== test session starts ====================================
platform linux -- Python 3.10.1, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /home/karthikeyan/stuff/python/myenv/bin/python
cachedir: .pytest_cache
rootdir: /tmp/testdir1
plugins: forked-1.4.0, typeguard-2.13.3, anyio-3.5.0, mypy-plugins-1.9.3, xdist-2.5.0
collected 2 items                                                                           

tests/test_foo.py::test_foo[123] PASSED                                               [ 50%]
tests/test_foo.py::test_foo[456.789] FAILED                                           [100%]

========================================= FAILURES ==========================================
_____________________________________ test_foo[456.789] _____________________________________

args = (), kwargs = {'arg': 456.789}, memo = <typeguard._CallMemo object at 0x7fd9fc7d2620>

    def wrapper(*args, **kwargs):
        memo = _CallMemo(python_func, _localns, args=args, kwargs=kwargs)
>       check_argument_types(memo)

/home/karthikeyan/stuff/python/myenv/lib/python3.10/site-packages/typeguard/__init__.py:1032: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

memo = <typeguard._CallMemo object at 0x7fd9fc7d2620>

    def check_argument_types(memo: Optional[_CallMemo] = None) -> bool:
        """
        Check that the argument values match the annotated types.
    
        Unless both ``args`` and ``kwargs`` are provided, the information will be retrieved from
        the previous stack frame (ie. from the function that called this).
    
        :return: ``True``
        :raises TypeError: if there is an argument type mismatch
    
        """
        if memo is None:
            # faster than inspect.currentframe(), but not officially
            # supported in all python implementations
            frame = sys._getframe(1)
    
            try:
                func = find_function(frame)
            except LookupError:
                return True  # This can happen with the Pydev/PyCharm debugger extension installed
    
            memo = _CallMemo(func, frame.f_locals)
    
        for argname, expected_type in memo.type_hints.items():
            if argname != 'return' and argname in memo.arguments:
                value = memo.arguments[argname]
                description = 'argument "{}"'.format(argname)
                try:
                    check_type(description, value, expected_type, memo)
                except TypeError as exc:  # suppress unnecessarily long tracebacks
>                   raise TypeError(*exc.args) from None
E                   TypeError: type of argument "arg" must be int; got float instead

/home/karthikeyan/stuff/python/myenv/lib/python3.10/site-packages/typeguard/__init__.py:875: TypeError
================================== short test summary info ==================================
FAILED tests/test_foo.py::test_foo[456.789] - TypeError: type of argument "arg" must be in...
================================ 1 failed, 1 passed in 0.14s ================================

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: proposal proposal for a new feature, often to gather opinions or design the API around the new feature
Projects
None yet
Development

No branches or pull requests

4 participants