mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 18:58:04 +00:00
[ty] Ignore ClassVar
declarations when resolving instance members (#18241)
## Summary Make sure that the following definitions all lead to the same outcome (bug originally noticed by @AlexWaygood) ```py from typing import ClassVar class Descriptor: def __get__(self, instance, owner) -> int: return 42 class C: a: ClassVar[Descriptor] b: Descriptor = Descriptor() c: ClassVar[Descriptor] = Descriptor() reveal_type(C().a) # revealed: int (previously: int | Descriptor) reveal_type(C().b) # revealed: int reveal_type(C().c) # revealed: int ``` ## Test Plan New Markdown tests
This commit is contained in:
parent
02fd48132c
commit
da4be789ef
2 changed files with 31 additions and 1 deletions
|
@ -777,6 +777,30 @@ reveal_type(C.variable_with_class_default1) # revealed: str
|
|||
reveal_type(c_instance.variable_with_class_default1) # revealed: str
|
||||
```
|
||||
|
||||
#### Descriptor attributes as class variables
|
||||
|
||||
Whether they are explicitly qualified as `ClassVar`, or just have a class level default, we treat
|
||||
descriptor attributes as class variables. This test mainly makes sure that we do *not* treat them as
|
||||
instance variables. This would lead to a different outcome, since the `__get__` method would not be
|
||||
called (the descriptor protocol is not invoked for instance variables).
|
||||
|
||||
```py
|
||||
from typing import ClassVar
|
||||
|
||||
class Descriptor:
|
||||
def __get__(self, instance, owner) -> int:
|
||||
return 42
|
||||
|
||||
class C:
|
||||
a: ClassVar[Descriptor]
|
||||
b: Descriptor = Descriptor()
|
||||
c: ClassVar[Descriptor] = Descriptor()
|
||||
|
||||
reveal_type(C().a) # revealed: int
|
||||
reveal_type(C().b) # revealed: int
|
||||
reveal_type(C().c) # revealed: int
|
||||
```
|
||||
|
||||
### Inheritance of class/instance attributes
|
||||
|
||||
#### Instance variable defined in a base class
|
||||
|
|
|
@ -1738,9 +1738,15 @@ impl<'db> ClassLiteral<'db> {
|
|||
let declared_and_qualifiers = symbol_from_declarations(db, declarations);
|
||||
match declared_and_qualifiers {
|
||||
Ok(SymbolAndQualifiers {
|
||||
symbol: declared @ Symbol::Type(declared_ty, declaredness),
|
||||
symbol: mut declared @ Symbol::Type(declared_ty, declaredness),
|
||||
qualifiers,
|
||||
}) => {
|
||||
// For the purpose of finding instance attributes, ignore `ClassVar`
|
||||
// declarations:
|
||||
if qualifiers.contains(TypeQualifiers::CLASS_VAR) {
|
||||
declared = Symbol::Unbound;
|
||||
}
|
||||
|
||||
// The attribute is declared in the class body.
|
||||
|
||||
let bindings = use_def.public_bindings(symbol_id);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue