Skip to content

Untyped decorator makes function "foo" untyped #11763

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

Closed
adamjstewart opened this issue Dec 16, 2021 · 17 comments
Closed

Untyped decorator makes function "foo" untyped #11763

adamjstewart opened this issue Dec 16, 2021 · 17 comments
Labels
bug mypy got something wrong

Comments

@adamjstewart
Copy link

Bug Report

Our CI tests started failing with the mypy 0.920 release. It looks like a new feature is checking decorator types? This may not be a bug in mypy but a lack of documentation explaining the new feature. I couldn't find any release notes or CHANGELOG explaining this feature.

To Reproduce

  1. Create a new file:
import pytest

@pytest.fixture
def foo() -> None:
    pass
  1. Run mypy: mypy --strict --ignore-missing-imports foo.py

Expected Behavior

Since the function is correctly typed, I wouldn't expect mypy to complain, but there must be a new feature triggering this. Is it because the pytest library is untyped? Normally mypy skips those kind of things.

Actual Behavior

Mypy complains with an error message I can't find in the documentation:

foo.py:3: error: Untyped decorator makes function "foo" untyped

Your Environment

  • Mypy version used: 0.920
  • Mypy command-line flags: --strict --ignore-missing-imports
  • Mypy configuration options from mypy.ini (and other config files): N/A
  • Python version used: 3.9.9
  • Operating system and version: macOS 10.15.7

@calebrob6

@adamjstewart adamjstewart added the bug mypy got something wrong label Dec 16, 2021
@nth10sd
Copy link

nth10sd commented Dec 16, 2021

What version of pytest are you using? pytest version 6.0.0rc1 onwards has type information.

@hauntsaninja
Copy link
Collaborator

I'm not aware of any changes in mypy 0.920 that would affect this. As Gary says, make sure you're using pytest>=6 and that it is installed in the same virtual environment as mypy (or that you use the --python-executable flag to point mypy to the python which has pytest installed)

@srittau
Copy link
Contributor

srittau commented Dec 16, 2021

Just as a point of reference: We also started getting decorator-related mypy errors with mypy 0.920:

_R = TypeVar("_R")
_P = ParamSpec("_P")

def transaction(f: Callable[Concatenate[Transaction, _P], _R]) -> Callable[_P, _R]: ...
@transaction
def foo(): ...
x = foo()  # Need type annotation for "x"  [var-annotated]

This didn't trigger an error before, although ParamSpec isn't properly supported. For now we changed the definition to the following to work around this:

def transaction(f: Callable[..., _R]) -> Callable[..., _R]: ...

@adamjstewart
Copy link
Author

I'm using pytest 6.2.5 (the latest version).

@adamjstewart
Copy link
Author

Based on @srittau's comment, is it safe to assume that this is a bug in mypy and not a lack of typing support in pytest? It seems like this error is raised even when the decorator is typed and no external libraries are used.

@pranavrajpal
Copy link
Contributor

What happens when you run mypy without --ignore-missing-imports? The only way I've been able to reproduce this is if pytest is not installed at all, which causes pytest.fixture to be silently turned into Any due to the --ignore-missing-imports flag, leading to the error message about an untyped decorator.

Based on @srittau's comment, is it safe to assume that this is a bug in mypy

That problem seems unrelated to your issue. Those errors are probably due to incomplete ParamSpec support, but pytest's type for fixture here doesn't seem to use ParamSpec.

@adamjstewart
Copy link
Author

What happens when you run mypy without --ignore-missing-imports?

I get an error saying it can't find type hints:

$ mypy --strict test.py 
test.py:1: error: Cannot find implementation or library stub for module named "pytest"
test.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports
test.py:3: error: Untyped decorator makes function "foo" untyped
Found 2 errors in 1 file (checked 1 source file)

However, pytest is installed and in both my PATH and PYTHONPATH:

$ which pytest
/Users/Adam/.spack/.spack-env/view/bin/pytest
$ python
Python 3.9.9 (main, Dec 15 2021, 16:29:14) 
[Clang 12.0.0 (clang-1200.0.32.29)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pytest
>>> pytest.__file__
'/Users/Adam/.spack/.spack-env/view/lib/python3.8/site-packages/pytest/__init__.py'

If I add --python-executable=$(which python) it doesn't seem to make a difference. I've reproduced this with both a Spack environment locally and in GitHub Actions CI (I'm assuming setup-python uses pip and virtualenv?). How does mypy find type hints?

@hauntsaninja
Copy link
Collaborator

What is the result of:

python -m pip show pytest
python -m pip show mypy
python -m mypy -c 'import pytest; reveal_type(pytest.fixture)'

@adamjstewart
Copy link
Author

$ python -m pip show pytest
Name: pytest
Version: 6.2.4
Summary: pytest: simple powerful testing with Python
Home-page: https://docs.pytest.org/en/latest/
Author: Holger Krekel, Bruno Oliveira, Ronny Pfannschmidt, Floris Bruynooghe, Brianna Laugher, Florian Bruhin and others
Author-email: None
License: MIT
Location: /Users/Adam/.spack/.spack-env/._view/tmizjnr2yebouizawyxzmo3gwignw5lz/lib/python3.8/site-packages
Requires: attrs, iniconfig, packaging, pluggy, py, toml
Required-by: pytest-mock, pytest-cov, nbmake

$ python -m pip show mypy
Name: mypy
Version: 0.920
Summary: Optional static typing for Python
Home-page: http://www.mypy-lang.org/
Author: Jukka Lehtosalo
Author-email: [email protected]
License: MIT License
Location: /Users/Adam/spack/opt/spack/darwin-catalina-ivybridge/apple-clang-12.0.0/py-mypy-0.920-fimyp26g6xfazrl4ibu5mvkwlwd33egu/lib/python3.9/site-packages
Requires: typing-extensions, mypy-extensions, tomli
Required-by: 

$ python -m mypy -c 'import pytest; reveal_type(pytest.fixture)'
<string>:1: note: Revealed type is "Any"

Note that this is on my local installation which uses Spack, not pip.

@hauntsaninja
Copy link
Collaborator

hauntsaninja commented Dec 16, 2021

I don't know anything about Spack, is it doing something weird to python? E.g., it looks like pytest is installed on Python 3.8 and mypy is installed on Python 3.9. So this definitely would not work without --python-executable.

If python is something unusual or shimmed, I'm not sure --python-executable will work.

@adamjstewart
Copy link
Author

Ah yes, Spack does many weird things. All packages are installed to a separate installation prefix allowing you to install multiple versions of the same package with different compilers. I just tried again with only Python 3.9 versions of mypy and pytest in my PYTHONPATH and I'm still getting the same error. Let me try on another system using conda/pip instead of Spack.

@adamjstewart
Copy link
Author

adamjstewart commented Dec 16, 2021

This seems to work inside a Conda environment:

$ python -m mypy -c 'import pytest; reveal_type(pytest.fixture)'
<string>:1: note: Revealed type is "Overload(def [_FixtureFunction <: def (*Any, **Any) -> builtins.object] (fixture_function: _FixtureFunction`-1, *, scope: Union[Union[Literal['session'], Literal['package'], Literal['module'], Literal['class'], Literal['function']], def (builtins.str, _pytest.config.Config) -> Union[Literal['session'], Literal['package'], Literal['module'], Literal['class'], Literal['function']]] =, params: Union[typing.Iterable[builtins.object], None] =, autouse: builtins.bool =, ids: Union[typing.Iterable[Union[None, builtins.str, builtins.float, builtins.int, builtins.bool]], def (Any) -> Union[builtins.object, None], None] =, name: Union[builtins.str, None] =) -> _FixtureFunction`-1, def (fixture_function: None =, *, scope: Union[Union[Literal['session'], Literal['package'], Literal['module'], Literal['class'], Literal['function']], def (builtins.str, _pytest.config.Config) -> Union[Literal['session'], Literal['package'], Literal['module'], Literal['class'], Literal['function']]] =, params: Union[typing.Iterable[builtins.object], None] =, autouse: builtins.bool =, ids: Union[typing.Iterable[Union[None, builtins.str, builtins.float, builtins.int, builtins.bool]], def (Any) -> Union[builtins.object, None], None] =, name: Union[builtins.str, None] =) -> _pytest.fixtures.FixtureFunctionMarker)"

Which raises the question of why it doesn't work with Spack or with GitHub Actions. The latter doesn't use Spack at all. Where does mypy search for type hints?

@adamjstewart
Copy link
Author

Where does mypy search for type hints?

I believe the answer to this question is https://mypy.readthedocs.io/en/stable/running_mypy.html#finding-imports

I'm guessing something gets found before the actual pytest .py files and that file is missing type hints. Does mypy have a way to print which step of the search process is being used?

@pranavrajpal
Copy link
Contributor

I'm guessing something gets found before the actual pytest .py files and that file is missing type hints.

I don't think this is what's happening. The earlier error message you posted said mypy couldn't find implementation or library stub for pytest, and if I remember correctly we emit a different error when we find a module but no type hints.

Spack does many weird things. All packages are installed to a separate installation prefix

I'm guessing this is why Spack doesn't work. mypy just searches the normal site-packages directories and ignores PYTHONPATH. #5701 has more information about that, and #11143 would fix this if it was merged. I'm just confused as to why an update to 0.920 would change anything, because it shouldn't have worked even before.

For Github Actions, do you have a CI run that I could look at with those errors? I've been looking at some recent CI runs on microsoft/torchgeo (which I'm guessing is the project this bug is taken from) but I can't find any untyped decorator errors.


Also, since this hasn't been mentioned yet: the errors you're getting are because of the --disallow-untyped-decorators flag, which is turned on by --strict. That means that one workaround for this issue would disabling that flag (although adding a type: ignore is probably easier).

@adamjstewart
Copy link
Author

mypy just searches the normal site-packages directories and ignores PYTHONPATH.

Ah, that's unfortunate. I'll just set export MYPYPATH="$PYTHONPATH" for now. Actually, I tried this and I'm seeing:

$ mypy .
mypy: "../.spack/.spack-env/view/lib/python3.8/site-packages/typing_extensions.py" shadows library module "typing_extensions"
note: A user-defined top-level module with name "typing_extensions" is not supported

Is that expected?

For Github Actions, do you have a CI run that I could look at with those errors? I've been looking at some recent CI runs on microsoft/torchgeo (which I'm guessing is the project this bug is taken from) but I can't find any untyped decorator errors.

Yep, that's the one. Here is one example, which occurs even after I force installation of mypy 0.910: https://github.com/microsoft/torchgeo/runs/4554198611?check_suite_focus=true. Now I'm trying to use 0.920 again and I'm no longer seeing the issue?? https://github.com/microsoft/torchgeo/runs/4556423997?check_suite_focus=true. I'm starting to lose my mind on this one lol.

@pranavrajpal
Copy link
Contributor

Is that expected?

I'm not sure. Judging from https://mypy.readthedocs.io/en/stable/stubs.html?highlight=site-packages#creating-a-stub, I don't think pointing MYPYPATH at the site-packages directory is supported:

You may be tempted to point MYPYPATH to the standard library or to the site-packages directory where your 3rd party packages are installed. This is almost always a bad idea – you will likely get tons of error messages about code you didn’t write and that mypy can’t analyze all that well yet, and in the worst case scenario mypy may crash due to some construct in a 3rd party package that it didn’t expect.

If I'm reading the docs correctly, Spack seems to be trying to emulate a regular virtualenv, so some ideas for getting this to work (after undoing the MYPYPATH change):

  • make sure mypy and pytest are both installed in the Spack environment
  • make sure that you have the environment activated when you run mypy and that you're using the environment's version of mypy

If that doesn't work, could you try seeing what the output of python -c 'import site; print(site.getsitepackages()); print(site.getusersitepackages())' is? I want to see where mypy is trying to look for packages.

Out of curiosity, is mypy not working with Spack a new occurrence, or has this always happened?


For the Github Actions errors, that seems to be unrelated to the mypy version. During the first CI run, the setup runs pip install .[datasets,style,train], which doesn't appear to include pytest, but the second CI run does pip install .[datasets,tests,train], which does include pytest. I'm not sure why those CI runs are running different commands, but that seems to be the reason you're getting all those Untyped decorator errors.

@hauntsaninja
Copy link
Collaborator

Closing, since I don't think there's anything actionable on mypy's side

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

5 participants