Skip to content

Commit

Permalink
Improve handling of __qualname__, __module__, and __class__
Browse files Browse the repository at this point in the history
  • Loading branch information
charliermarsh committed May 18, 2023
1 parent 728c881 commit c9c0713
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 9 deletions.
7 changes: 0 additions & 7 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4827,13 +4827,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
Expand Down
44 changes: 44 additions & 0 deletions crates/ruff/src/rules/pyflakes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -470,6 +470,16 @@ mod tests {
"#,
&[Rule::UndefinedName],
);
flakes(
r#"
def f():
__qualname__ = 1
class Foo:
__qualname__
"#,
&[Rule::UnusedVariable],
);
}

#[test]
Expand Down Expand Up @@ -1148,6 +1158,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>
Expand Down
26 changes: 24 additions & 2 deletions crates/ruff_python_semantic/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,22 @@ impl<'a> Context<'a> {
/// Resolve a reference to the given symbol.
pub fn resolve_reference(&mut self, symbol: &str, range: TextRange) -> ResolvedReference {
let mut first_iter = true;
let mut seen_function = false;
let mut import_starred = false;
for scope_id in self.scopes.ancestor_ids(self.scope_id) {
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;
} else if !first_iter {
}
if !first_iter {
continue;
}
}
Expand Down Expand Up @@ -152,7 +161,20 @@ 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__)
// ```
if scope.kind.is_class() {
if first_iter && matches!(symbol, "__module__" | "__qualname__") {
return ResolvedReference::ImplicitGlobal;
}
}

first_iter = false;
seen_function |= scope.kind.is_function();
import_starred = import_starred || scope.uses_star_imports();
}

Expand Down

0 comments on commit c9c0713

Please sign in to comment.