mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-31 15:47:41 +00:00
[ty] Respect MRO_NO_OBJECT_FALLBACK
policy when looking up symbols on type
instances (#18312)
## Summary This should address a problem that came up while working on https://github.com/astral-sh/ruff/pull/18280. When looking up an attribute (typically a dunder method) with the `MRO_NO_OBJECT_FALLBACK` policy, the attribute is first looked up on the meta type. If the meta type happens to be `type`, we go through the following branch in `find_name_in_mro_with_policy`:97ff015c88/crates/ty_python_semantic/src/types.rs (L2565-L2573)
The problem is that we now look up the attribute on `object` *directly* (instead of just having `object` in the MRO). In this case, `MRO_NO_OBJECT_FALLBACK` has no effect in `class_member_from_mro`:c3feb8ce27/crates/ty_python_semantic/src/types/class.rs (L1081-L1082)
So instead, we need to explicitly respect the `MRO_NO_OBJECT_FALLBACK` policy here by returning `Symbol::Unbound`. ## Test Plan Added new Markdown tests that explain the ecosystem changes that we observe.
This commit is contained in:
parent
d078ecff37
commit
4ef2c223c9
2 changed files with 40 additions and 3 deletions
|
@ -305,4 +305,37 @@ def _(c: Callable[[int], int]):
|
|||
reveal_type(c.__call__) # revealed: (int, /) -> int
|
||||
```
|
||||
|
||||
Unlike other type checkers, we do _not_ allow attributes to be accessed that would only be available
|
||||
on function-like callables:
|
||||
|
||||
```py
|
||||
def f_wrong(c: Callable[[], None]):
|
||||
# error: [unresolved-attribute] "Type `() -> None` has no attribute `__qualname__`"
|
||||
c.__qualname__
|
||||
|
||||
# error: [unresolved-attribute] "Unresolved attribute `__qualname__` on type `() -> None`."
|
||||
c.__qualname__ = "my_callable"
|
||||
```
|
||||
|
||||
We do this, because at runtime, calls to `f_wrong` with a non-function callable would raise an
|
||||
`AttributeError`:
|
||||
|
||||
```py
|
||||
class MyCallable:
|
||||
def __call__(self) -> None:
|
||||
pass
|
||||
|
||||
f_wrong(MyCallable()) # raises `AttributeError` at runtime
|
||||
```
|
||||
|
||||
If users want to write to attributes such as `__qualname__`, they need to check the existence of the
|
||||
attribute first:
|
||||
|
||||
```py
|
||||
def f_okay(c: Callable[[], None]):
|
||||
if hasattr(c, "__qualname__"):
|
||||
c.__qualname__ # okay
|
||||
c.__qualname__ = "my_callable" # also okay
|
||||
```
|
||||
|
||||
[gradual form]: https://typing.python.org/en/latest/spec/glossary.html#term-gradual-form
|
||||
|
|
|
@ -2567,9 +2567,13 @@ impl<'db> Type<'db> {
|
|||
// `Type::NominalInstance(type)` is equivalent to looking up the name in the
|
||||
// MRO of the class `object`.
|
||||
Type::NominalInstance(instance) if instance.class.is_known(db, KnownClass::Type) => {
|
||||
KnownClass::Object
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
if policy.mro_no_object_fallback() {
|
||||
Some(Symbol::Unbound.into())
|
||||
} else {
|
||||
KnownClass::Object
|
||||
.to_class_literal(db)
|
||||
.find_name_in_mro_with_policy(db, name, policy)
|
||||
}
|
||||
}
|
||||
|
||||
Type::FunctionLiteral(_)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue