简体   繁体   中英

How to specify mypy types for pytest fixtures

I am trying to specify mypy type hints for the pytest native fixtures I am using in my test project eg:

import pytest

def pytest_configure(config):
    # Do something useful here

The config fixture returns a _pytest.config.Config object. If I try to model this naively:

import pytest

def pytest_configure(config: Config) -> None:
    # Do something useful here

I receive a mypy error: conftest.py:3: error: Name 'Config' is not defined [name-defined]

I could do from _pytest.config import Config , but this doesn't seem to be a good way, because _pytest is private. Another option would be to ignore the type with # type: ignore . If this is the recommended way I would of course do this, but I wonder if there is a better option.

I have the same issues in with any kind of pytest native fixtures I use, eg request which is used for parameterized fixtures. This would be a _pytest.fixtures.FixtureRequest .

Importing from _pytest.config

Since pytest doesn't currently export Config (as of 6.2), the only way for typing is to use from _pytest.config import Config . This is how I also type config , as can be seen eg in this question of mine :

from _pytest.config import Config

def pytest_configure(config: Config) -> None:
    ...

You can track the typing progress in this pytest issue: #7469 .

Custom type stubs

You can also introduce a small custom type stub that hides the reexport. It's questionable whether it will be useful here, only worth to mention for an alternative solution. If you create a file _typeshed/pytest.pyi with the following contents:

from typing import Any
from _pytest.config import Config as Config

def __getattr__(name: str) -> Any: ...  # incomplete

and make it accessible to mypy in mypy.ini :

[mypy]
mypy_path = _typeshed

Now you can import from pytest import Config at least in type checking mode - the runtime import will still fail. So the imports would look like

from typing import Any, TYPE_CHECKING

if TYPE_CHECKING:
    from pytest import Config
else:
    Config = Any


def pytest_configure(config: Config) -> None:
    pass

The only benefit of that solution is that the private import is now hidden; I'd still go with the private import though.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM