diff --git a/crates/ty_python_semantic/resources/mdtest/attributes.md b/crates/ty_python_semantic/resources/mdtest/attributes.md index acd0799c28..40a1841c67 100644 --- a/crates/ty_python_semantic/resources/mdtest/attributes.md +++ b/crates/ty_python_semantic/resources/mdtest/attributes.md @@ -1462,6 +1462,14 @@ Instance attributes also take precedence over the `__getattr__` method: reveal_type(c.instance_attr) # revealed: str ``` +Importantly, `__getattr__` is only called if attributes are accessed on instances, not if they are +accessed on the class itself: + +```py +# error: [unresolved-attribute] +CustomGetAttr.whatever +``` + ### Type of the `name` parameter If the `name` parameter of the `__getattr__` method is annotated with a (union of) literal type(s), diff --git a/crates/ty_python_semantic/resources/mdtest/named_tuple.md b/crates/ty_python_semantic/resources/mdtest/named_tuple.md index d31ba81b3b..b7cf17ea46 100644 --- a/crates/ty_python_semantic/resources/mdtest/named_tuple.md +++ b/crates/ty_python_semantic/resources/mdtest/named_tuple.md @@ -149,3 +149,20 @@ Person = namedtuple("Person", ["id", "name", "age"], defaults=[None]) alice = Person(1, "Alice", 42) bob = Person(2, "Bob") ``` + +## NamedTuple with custom `__getattr__` + +This is a regression test for . Make sure that the +`__getattr__` method does not interfere with the `NamedTuple` behavior. + +```py +from typing import NamedTuple + +class Vec2(NamedTuple): + x: float = 0.0 + y: float = 0.0 + + def __getattr__(self, attrs: str): ... + +Vec2(0.0, 0.0) +``` diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 94c3bfd9d7..1ffe70873a 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -4516,7 +4516,8 @@ impl<'db> Type<'db> { .member_lookup_with_policy( db, "__init__".into(), - MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK, + MemberLookupPolicy::NO_INSTANCE_FALLBACK + | MemberLookupPolicy::MRO_NO_OBJECT_FALLBACK, ) .symbol .is_unbound()