As reported by others, the fact that fixture can exist magically is confusing for many users unfamiliar with pytest and it can be gruesome to track in larger projects with many fixtures.
Since type annotation is working well in python 3 I was thinking that one explicit way would be to have a new Fixture type to help annotate the fixture arguments.
from typing import Union
import pytest
Fixture = Union
@pytest.fixture
def bob() -> str:
return '42'
@pytest.fixture
def alice() -> int:
return 42
def test_foo(bob: Fixture[str], alice: Fixture[int]):
assert bob != alice
In this example I 'abuse' Union so that existing tools are taking the hinting without any issue. For the person coming in and reading the code, especially in larger projects, the fact that the arguments are fixtures becomes very explicit and in the spirit of the Zen of Python:
Explicit is better than implicit.
Unfortunately mypy and other type checking tools don't seem to 'alias' Union since it is a special case.
This on the other hand works but I would prefer the annotation of the first example:
from typing import Union
import pytest
@pytest.fixture
def bob() -> str:
return '42'
@pytest.fixture
def alice() -> int:
return 42
FixtureStr = Union[str]
FixtureInt = Union[int]
def test_bar(bob: FixtureStr, alice: FixtureInt):
assert bob != alice
IDEs such as PyCharm then now able to hint on bob being a string and as a user I can tell that it is a fixture argument.
Maybe a mypy plugin could help here somehow?
(What already works of course is using the returned type of the fixture directly - but it does not make it obvious that it is a fixture then, of course.)
/cc @bluetech
I haven't actually tested it, but you should be able to do something like:
T = typing.TypeVar('T')
class Fixture(typing.Generic[T]):
pass
def test_bar(bob: Fixture[str]):
...
Why not just write it as def test_bar(bob: str): ... at that point? IMO the Fixture just makes it more confusing.
I just finally have to write pytest support in Jedi. I'm using both tools daily so I should really just do myself a favor :).
def test_bar(bob: str): is what we are doing today but while pytest is common, its use of magical fixtures is confusing for the majority of python developers that do not know about them.
I was refactoring some code to comply with PEP-8 recently so that pylint and other tools can better expose potential bugs but I had to double and triple check which arguments were safe to rename and which were not. The Fixture annotation would also allow us to better target which arguments need to be updated when renaming a fixture. For example to rename the bob fixture we could grep for bob: Fixture and have a much higher confidence that all the code that needed updating got updated.
Florian's solution passes mypy but then it does not allow PyCharm to detect that bob is a string and we have no type hinting, similar result as the Fixture = Union approach.
This would have to be a mypy plugin... and even then, it's going to be full of edge cases. IMO if you need that level of visibility fixtures are just not suitable.
I second @davidhalter's message -- I don't think a special syntax improves the situation (and would require a mypy plugin to undo the outer Fixture[...] indirection to "unwrap" the inner type)
Support for fixtures just landed in Jedi's master branch so it is only a matter of time until Jedi is released and you all get to have IDE support for fixtures. Annotations are not necessary (but might help in very complicated cases).
Thanks everyone for chiming in here, I think it was an useful discussion, but there's nothing really to be done on pytest AFAIU.
Most helpful comment
Support for fixtures just landed in Jedi's master branch so it is only a matter of time until Jedi is released and you all get to have IDE support for fixtures. Annotations are not necessary (but might help in very complicated cases).