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

Upper bound is missing from generic function signature in error report #17792

Open
jolaf opened this issue Sep 19, 2024 · 1 comment
Open

Upper bound is missing from generic function signature in error report #17792

jolaf opened this issue Sep 19, 2024 · 1 comment
Labels
bug mypy got something wrong

Comments

@jolaf
Copy link
Contributor

jolaf commented Sep 19, 2024

Bug Report

The provided test defines the generic function f() in two ways which are incompatible with each other – one has an upper bound, and the other has not.

However, when reporting this incompatibility, mypy prints two signatures that look equivalent because the upper bound is not mentioned in one of them.

This makes it very difficult to understand why mypy reports an error and how to fix it. Moreover, the error report looks like a bug in mypy.

To Reproduce

https://mypy-play.net/?mypy=1.11.2&python=3.12&enable-incomplete-feature=NewGenericSyntax&gist=06224b5d473a6be86cae73f00e4643f1

@jolaf jolaf added the bug mypy got something wrong label Sep 19, 2024
@jolaf jolaf changed the title Generic upper bound is missing from function signature in error report Upper bound is missing from generic function signature in error report Sep 19, 2024
@brianschubert
Copy link

brianschubert commented Sep 20, 2024

For reference, the reproducer from my mypy playground link above is:

from collections.abc import Callable
from random import choice
from typing import Any, TypeVar

if choice((True, False)):
    F = TypeVar('F')

    def f(a: F) -> F:
        return a
else:
    def f[F_: Callable[..., Any]](a: F_) -> F_:
        return a

which currently outputs

main.py:11: error: All conditional function variants must have identical signatures  [misc]
main.py:11: note: Original:
main.py:11: note:     def [F] f(a: F) -> F
main.py:11: note: Redefinition:
main.py:11: note:     def [F_] f(a: F_) -> F_
Found 1 error in 1 file (checked 1 source file)

The issue is that the bound for F_ isn't being displayed.


The root cause seems to be this part of pretty_callable:

mypy/mypy/messages.py

Lines 2943 to 2949 in a646f33

if isinstance(tvar, TypeVarType):
upper_bound = get_proper_type(tvar.upper_bound)
if (
isinstance(upper_bound, Instance)
and upper_bound.type.fullname != "builtins.object"
):
tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}")

The relevant bit is the isinstance(upper_bound, Instance) check. This means that the bound for the type var is only displayed if it's a subtype of Instance, which CallableType is not.

A quick fix would be to simply add a check for CallableType, as in:

          if isinstance(tvar, TypeVarType):
              upper_bound = get_proper_type(tvar.upper_bound)
              if (
                  isinstance(upper_bound, Instance)
                  and upper_bound.type.fullname != "builtins.object"
-               ):
+               ) or isinstance(upper_bound, CallableType):
                  tvars.append(f"{tvar.name}: {format_type_bare(upper_bound, options)}")

which would change the output to

main.py:11: error: All conditional function variants must have identical signatures  [misc]
main.py:11: note: Original:
main.py:11: note:     def [F] f(a: F) -> F
main.py:11: note: Redefinition:
main.py:11: note:     def [F: Callable[..., Any]] f(a: F) -> F
Found 1 error in 1 file (checked 1 source file)

However, this wouldn't be a complete fix, since there are other ProperType (but not Instance) subtypes that should probably also be displayed as bounds. At a glance I think UnionType, LiteralType, TupleType, and NoneType all fall in this category, in addition to CallableType.

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

2 participants