mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-15 16:10:38 +00:00
Improve handling of __qualname__
, __module__
, and __class__
(#4512)
This commit is contained in:
parent
9e21414294
commit
6aa9900c03
3 changed files with 75 additions and 8 deletions
|
@ -4841,13 +4841,6 @@ impl<'a> Checker<'a> {
|
|||
return;
|
||||
}
|
||||
|
||||
// Allow "__module__" and "__qualname__" in class scopes.
|
||||
if (id == "__module__" || id == "__qualname__")
|
||||
&& matches!(self.ctx.scope().kind, ScopeKind::Class(..))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid flagging if `NameError` is handled.
|
||||
if self
|
||||
.ctx
|
||||
|
|
|
@ -473,6 +473,16 @@ mod tests {
|
|||
"#,
|
||||
&[Rule::UndefinedName],
|
||||
);
|
||||
flakes(
|
||||
r#"
|
||||
def f():
|
||||
__qualname__ = 1
|
||||
|
||||
class Foo:
|
||||
__qualname__
|
||||
"#,
|
||||
&[Rule::UnusedVariable],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1151,6 +1161,40 @@ mod tests {
|
|||
"#,
|
||||
&[],
|
||||
);
|
||||
flakes(
|
||||
r#"
|
||||
class Test(object):
|
||||
print(__class__.__name__)
|
||||
|
||||
def __init__(self):
|
||||
self.x = 1
|
||||
|
||||
t = Test()
|
||||
"#,
|
||||
&[Rule::UndefinedName],
|
||||
);
|
||||
flakes(
|
||||
r#"
|
||||
class Test(object):
|
||||
X = [__class__ for _ in range(10)]
|
||||
|
||||
def __init__(self):
|
||||
self.x = 1
|
||||
|
||||
t = Test()
|
||||
"#,
|
||||
&[Rule::UndefinedName],
|
||||
);
|
||||
flakes(
|
||||
r#"
|
||||
def f(self):
|
||||
print(__class__.__name__)
|
||||
self.x = 1
|
||||
|
||||
f()
|
||||
"#,
|
||||
&[Rule::UndefinedName],
|
||||
);
|
||||
}
|
||||
|
||||
/// See: <https://github.com/PyCQA/pyflakes/blob/04ecb0c324ef3b61124e2f80f9e1af6c3a4c7b26/pyflakes/test/test_imports.py>
|
||||
|
|
|
@ -126,11 +126,19 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let mut seen_function = false;
|
||||
let mut import_starred = false;
|
||||
for (index, scope_id) in self.scopes.ancestor_ids(self.scope_id).enumerate() {
|
||||
let scope = &self.scopes[scope_id];
|
||||
if scope.kind.is_class() {
|
||||
if symbol == "__class__" {
|
||||
// Allow usages of `__class__` within methods, e.g.:
|
||||
//
|
||||
// ```python
|
||||
// class Foo:
|
||||
// def __init__(self):
|
||||
// print(__class__)
|
||||
// ```
|
||||
if seen_function && matches!(symbol, "__class__") {
|
||||
return ResolvedReference::ImplicitGlobal;
|
||||
}
|
||||
if index > 0 {
|
||||
|
@ -162,6 +170,28 @@ impl<'a> Context<'a> {
|
|||
return ResolvedReference::Resolved(scope_id, *binding_id);
|
||||
}
|
||||
|
||||
// Allow usages of `__module__` and `__qualname__` within class scopes, e.g.:
|
||||
//
|
||||
// ```python
|
||||
// class Foo:
|
||||
// print(__qualname__)
|
||||
// ```
|
||||
//
|
||||
// Intentionally defer this check to _after_ the standard `scope.get` logic, so that
|
||||
// we properly attribute reads to overridden class members, e.g.:
|
||||
//
|
||||
// ```python
|
||||
// class Foo:
|
||||
// __qualname__ = "Bar"
|
||||
// print(__qualname__)
|
||||
// ```
|
||||
if index == 0 && scope.kind.is_class() {
|
||||
if matches!(symbol, "__module__" | "__qualname__") {
|
||||
return ResolvedReference::ImplicitGlobal;
|
||||
}
|
||||
}
|
||||
|
||||
seen_function |= scope.kind.is_function();
|
||||
import_starred = import_starred || scope.uses_star_imports();
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue