[ty] Fall back to object for attribute access on synthesized protocols (#20286)

This commit is contained in:
Alex Waygood 2025-09-08 13:04:37 +01:00 committed by GitHub
parent 982a0a2a7c
commit deb3d3d150
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 39 additions and 2 deletions

View file

@ -392,8 +392,14 @@ from inspect import getattr_static
def f_okay(c: Callable[[], None]):
if hasattr(c, "__qualname__"):
c.__qualname__ # okay
reveal_type(c.__qualname__) # revealed: object
# TODO: should be `property`
# (or complain that we don't know that `type(c)` has the attribute at all!)
reveal_type(type(c).__qualname__) # revealed: @Todo(Intersection meta-type)
# `hasattr` only guarantees that an attribute is readable.
#
# error: [invalid-assignment] "Object of type `Literal["my_callable"]` is not assignable to attribute `__qualname__` on type `(() -> None) & <Protocol with members '__qualname__'>`"
c.__qualname__ = "my_callable"

View file

@ -84,3 +84,17 @@ def _(obj: MaybeWithSpam):
# error: [possibly-unbound-attribute]
reveal_type(obj.spam) # revealed: int
```
All attribute available on `object` are still available on these synthesized protocols, but
attributes that are not present on `object` are not available:
```py
def f(x: object):
if hasattr(x, "__qualname__"):
reveal_type(x.__repr__) # revealed: bound method object.__repr__() -> str
reveal_type(x.__str__) # revealed: bound method object.__str__() -> str
reveal_type(x.__dict__) # revealed: dict[str, Any]
# error: [unresolved-attribute] "Type `<Protocol with members '__qualname__'>` has no attribute `foo`"
reveal_type(x.foo) # revealed: Unknown
```