python / mypy

Optional static typing for Python
https://www.mypy-lang.org/
Other
18.5k stars 2.83k forks source link

Methods with the same name as a type sometimes cause error "not valid as a type" #15047

Open tilsche opened 1 year ago

tilsche commented 1 year ago

Bug Report

When using a decorator on a class method with the same name as a type used in its return type, and the method is decorated with a decorator that spans multiple lines, an error is thrown.

What puzzles me is that this only causes an error if the decorator spans multiple lines, not if it is just on a single line.

Neither pylint nur flake8 do complain about having a method that shadows another name in general. So I am assuming that this should be generally ok.

To Reproduce

class Foo:
    @property
    # the existence of this comment triggers the error
    def int(self) -> int:
        return 0

Some more examples show that this

Expected Behavior

Use the types from the outer scope to construct the return type and validate correctly.

Actual Behavior

error: Function "__main__.Foo.int" is not valid as a type  [valid-type]
note: Perhaps you need "Callable[...]" or a callback protocol?

Your Environment

john-bodley commented 1 year ago

This example seems somewhat similar, i.e.,

class Foo:
    @property
    def type(self) -> str:
        return "foo"

is valid, however,

class Foo:
    @property
    def type(self) -> str:
        return "foo"

    @property
    def foo(self) -> type:
        return Foo

the inclusion of the foo property results in the following Mypy error:

error: Function "__main__.Foo.type" is not valid as a type  [valid-type]
note: Perhaps you need "Callable[...]" or a callback protocol?
hauntsaninja commented 1 year ago

In your case, mypy's behaviour matches that of Python's scoping, no?

In [1]: class Foo:
   ...:     def type(self) -> str:
   ...:         return "foo"
   ...: 
   ...:     def foo(self) -> type:
   ...:         return Foo
   ...: 

In [2]: Foo.foo.__annotations__
Out[2]: {'return': <function __main__.Foo.type(self) -> str>}

OP's case is definitely buggy, because no matter what mypy's behaviour is it should be robust to the presence of comments and whitespace

john-bodley commented 1 year ago

Thanks @hauntsaninja for the response. What's your recommendation for a preferred solution? Is it simply to refer to the built-in, i.e.,

In [1]: class Foo:
   ...:     def type(self) -> str:
   ...:         return "foo"
   ...: 
   ...:     def foo(self) -> __builtins__.type:
   ...:         return Foo
   ...: 

In [2]: Foo.foo.__annotations__
Out[2]: {'return': type}
JelleZijlstra commented 1 year ago

Using builtins.type (and import builtins) is what I'd recommend.