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

"Missing return statement" caused by context manager? #7577

Closed
jonapich opened this issue Sep 27, 2019 · 4 comments · Fixed by #7655
Closed

"Missing return statement" caused by context manager? #7577

jonapich opened this issue Sep 27, 2019 · 4 comments · Fixed by #7655

Comments

@jonapich
Copy link

jonapich commented Sep 27, 2019

""" Unit tests. """

from types import TracebackType
from typing import Optional, Type


class TestManager:
    def __enter__(self) -> None:
        ...

    def __exit__(self, exc_type: Optional[Type[BaseException]], 
                 exc_val: Optional[BaseException],
                 exc_tb: Optional[TracebackType]) -> bool:
        ...


def func() -> int:
    with TestManager():
        return 1

Mypy will complain about func(), saying it doesn't have a return statement.

tests/__init__.py:16:1: error: Missing return statement

Is this intended?

Note: I also tried class TestManager(ContextManager[None]): just to see if that would help mypy but it didn't change anything.

@ilevkivskyi
Copy link
Member

If the TestManager can "swallow" exceptions, then this is a valid error. Otherwise you should annotate the __exit__() as returning Literal[False] (see 0.730 release notes).

@gvanrossum
Copy link
Member

gvanrossum commented Sep 27, 2019 via email

@ilevkivskyi
Copy link
Member

I wonder if we should just roll this back.

It is not as easy as that, since reverting will cause false positives (like falsely complaining about unreachable code).

Or maybe the error message could have a note pointing to this solution, if there's a context manager involved?

Unfortunately this may be not easy due to how we track the control flow (binder). We can move the docs to a more prominent location.

@JukkaL
Copy link
Collaborator

JukkaL commented Sep 30, 2019

I agree that we should do something about this. I think that reverting is fine, at least temporarily, since I generally prefer preserving existing "questionable" behavior to introducing new, different "questionable" behavior (to avoid friction) -- unless we can expect that the new behavior occurs much less frequently. It would be better to fix this or to improve the generated error message, however.

Note that a None return also works, and this may be preferable to using Literal[False]. This requires a semantic change to code, though.

Here are some ideas about how we could make this better without reverting:

  1. If an__exit__ method is annotated to return bool, but all the return statement are return False, generate an error and require annotating the return type as Literal[False] (or None).
  2. If we get a missing return statement error, look for all 'with' statements in the function, and if one of these uses a context manager with a bool return in __exit__, explain how this might be causing the issue, and suggest changing the return type of __exit__ to Literal[False] or None.
  3. If an __exit__ method is annotated to return bool, but it always returns False, flag this and somehow treat the return type implicitly as Literal[False] when type checking 'with' statements.
  4. Document this in the mypy docs. This is probably necessary but likely not sufficient, since the current behavior is pretty unintuitive.

Only 2 and 4 help with stubs. My current preference would be to implement 1, 4 and 2 (in this order). 3 seems too magical and ad hoc.

@JukkaL JukkaL reopened this Sep 30, 2019
@JukkaL JukkaL self-assigned this Oct 4, 2019
JukkaL added a commit that referenced this issue Oct 9, 2019
…7655)

Mypy can give false positives about missing return statements
if `__exit__` that always returns `False` is annotated to return
`bool` instead of `Literal[False]`.

Add new error code and documentation for the error code since
this error condition is not very obvious.

Fixes #7577.

There are two major limitations:

1. This doesn't support async context managers.
2. This won't help if a stub has an invalid `__exit__` return type.

I'll create a follow-up issues about the above.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants