Skip to content

Incorrect descriptor lookup for methods on types that are not disjoint from None #737

@AlexWaygood

Description

@AlexWaygood

Summary

Other than object, all nominal instance types are disjoint from None. But... this is not true for instances of structural (Protocol) types -- and it turns out that this causes issues for our current implementation of the descriptor protocol.

For example, compare and contrast the different behaviour of these two protocols. SupportsFoo is disjoint from None; SupportsStr is not:

from typing import Protocol

class SupportsFoo(Protocol):
    def foo(self) -> str: ...

class SupportsStr(Protocol):
    def __str__(self) -> str: ...

def f(f: SupportsFoo, s: SupportsStr):
    reveal_type(f.foo)       # revealed: `bound method SupportsFoo.foo() -> str`
    f.foo()                  # no diagnostic

    reveal_type(s.__str__)   # revealed: `def __str__(self) -> str`
    s.__str__()              # error: No argument provided for required parameter `self` of function `__str__` (missing-argument)

https://play.ty.dev/09505473-95b5-4b51-b992-7784595bcf55

Whether or not a type is disjoint from None impacts our understanding of the way the descriptor protocol is invoked when a method is accessed on that type. The type that a method is accessed on is passed to FunctionType.__get__ when we invoke the descriptor protocol; if that type is not disjoint from None, we end up having to pick the first overload here, which means that the attribute access is (incorrectly!) evaluated as resolving to the original function object rather than a bound method:

https://github.com/astral-sh/ruff/blob/c6fd11fe3694646c7b5667e37dfc67b114e2f50a/crates/ty_python_semantic/src/types.rs#L3526-L3575

Many thanks to @sharkdp who helped me track this bug down over the last couple of days!

Version

c6fd11f

Metadata

Metadata

Assignees

No one assigned

    Labels

    Protocolsattribute accessInstance attributes, class attributes, etc.bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions