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

mypy false positive with custom property descriptors #7153

Closed
mostrows2 opened this issue Jul 4, 2019 · 3 comments
Closed

mypy false positive with custom property descriptors #7153

mostrows2 opened this issue Jul 4, 2019 · 3 comments
Labels
bug mypy got something wrong priority-1-normal topic-descriptors Properties, class vs. instance attributes

Comments

@mostrows2
Copy link

Note: if you are reporting a wrong signature of a function or a class in
the standard library, then the typeshed tracker is better suited
for this report: https://github.com/python/typeshed/issues

Please provide more information to help us understand the issue:

  • Are you reporting a bug, or opening a feature request?

Bug.

  • Please insert below the code you are checking with mypy,
    or a mock-up repro if the source is private. We would appreciate
    if you try to simplify your case to a minimal repro.

See below.

  • What is the actual behavior/output?

See below.

  • What is the behavior/output you expect?

Error as shown n output should not be reported.

  • What are the versions of mypy and Python you are using?

mypy 0.711, python 3.7.3

  • What are the mypy flags you are using? (For example --strict-optional)

None.


It doesn't seem to be possible to create an explicit descriptor class that behaves
the same way as @Property does with respect to methods of sub-types declaring more constrained return types.

In the example below, we have classes "Obj" > "Foo", and "AA" > "BB".
All methods of Obj return AA. Foo overrides all methods of Obj, but all of those methods
return "BB". Note that all methods of Foo are still consistent with the definitions in
Obj.

The method func1 has no decorators and is accepted without complaints, and its types are correctly recognized.

Method func3 is decorated with "@Property". Here too, mypy handles this case without error.

Method func2 is decorated with "@prop", which is a custom property descriptor.
The revealed types for func2 are consistent with func3 -- hence the typing for the "prop" class appears to be consistent with what "@Property" does. However, mypy flags an error that
Foo.func2 is not consistent with the super type definition Obj.func2. (As a related issue, I haven't been able to come up with a better typing scheme for "prop" without using "type:ignore", which still gives the same type results.)

In this example below, I'd expect mypy to report no errors on either func2 or func3, or to report errors for both. As it stands, it appears that it's not possible to create a typing scheme for a descriptor that fully mirrors mypy's special handling for "@Property".


from typing import Generic, TypeVar,Type, Callable,Any, Union, overload,Optional

OwnerType = TypeVar("OwnerType", bound=object)
T = TypeVar('T', covariant=True)
CallableType = TypeVar("CallableType", bound=Callable)


class prop(Generic[T]):
    def __init__(self, func: Callable[[Any], T]):
        self.func = func

    def __get__(self, obj: Any, owner: Type[Any]) -> T:
        if obj is None:
            return self # type: ignore
        return self.func(obj)

class AA:
    def foo(self) -> int:
        return 1

class BB(AA):
    def bar(self) -> int:
        return 1

    
class Obj:
    def func1(self) -> AA:
        return AA()
    @prop
    def func2(self) -> AA:
        return AA()
    @property
    def func3(self) -> AA:
        return AA()        

class Foo(Obj):
    def func1(self) -> BB:
        return BB()
    @prop
    def func2(self) -> BB:
        return BB()
    @property
    def func3(self) -> BB:
        return BB()    


x = Obj()
y = Foo()
reveal_type(Obj.func1)
reveal_type(Obj.func2)
reveal_type(Obj.func3)
reveal_type(Foo.func1)
reveal_type(Foo.func2)
reveal_type(Foo.func3)
reveal_type(x.func1)
reveal_type(x.func2)
reveal_type(x.func3)
reveal_type(y.func1)
reveal_type(y.func2)
reveal_type(y.func3)

Mypy output:

/tmp/xx.py:40: error: Signature of "func2" incompatible with supertype "Obj"
/tmp/xx.py:49: note: Revealed type is 'def (self: xx.Obj) -> xx.AA'
/tmp/xx.py:50: note: Revealed type is 'xx.AA*'
/tmp/xx.py:51: note: Revealed type is 'def (self: xx.Obj) -> xx.AA'
/tmp/xx.py:52: note: Revealed type is 'def (self: xx.Foo) -> xx.BB'
/tmp/xx.py:53: note: Revealed type is 'xx.BB*'
/tmp/xx.py:54: note: Revealed type is 'def (self: xx.Foo) -> xx.BB'
/tmp/xx.py:55: note: Revealed type is 'def () -> xx.AA'
/tmp/xx.py:56: note: Revealed type is 'xx.AA*'
/tmp/xx.py:57: note: Revealed type is 'xx.AA'
/tmp/xx.py:58: note: Revealed type is 'def () -> xx.BB'
/tmp/xx.py:59: note: Revealed type is 'xx.BB*'
/tmp/xx.py:60: note: Revealed type is 'xx.BB'

@ilevkivskyi
Copy link
Member

To avoid the first # type: ignore you need to use an overload (we have an issue to add some docs about this #2566).

As for the second problem, this looks like a mystery. I think this is relatively low priority, unless this turns out easy to fix.

@ilevkivskyi ilevkivskyi added bug mypy got something wrong priority-2-low labels Aug 12, 2019
@ilevkivskyi
Copy link
Member

OK, here is a possible explanation. It looks like mypy prohibits covariant overriding of mutable attributes only if they are defined as methods (see however #3208). So to fix this we jut need to special-case descriptors with __get__ only, since these are read-only.

@AlexWaygood
Copy link
Member

Can't reproduce this issue on mypy 0.941 (and can on 0.711); this appears to be fixed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong priority-1-normal topic-descriptors Properties, class vs. instance attributes
Projects
None yet
Development

No branches or pull requests

4 participants