Skip to content

Commit c62184d

Browse files
authored
'Revert "F821: Fix false negatives in .py files when from __future__ import annotations is active (#10362)"' (#10513)
1 parent 9b3c732 commit c62184d

File tree

6 files changed

+50
-41
lines changed

6 files changed

+50
-41
lines changed

crates/ruff_linter/resources/test/fixtures/pyflakes/F821_27.py

-13
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,3 @@ class MyClass:
3333
baz: MyClass
3434
eggs = baz # Still invalid even when `__future__.annotations` are enabled
3535
eggs = "baz" # always okay
36-
37-
# Forward references:
38-
MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled
39-
MaybeDStr2: TypeAlias = Optional["DStr"] # always okay
40-
DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled
41-
DStr2: TypeAlias = Union["D", str] # always okay
42-
43-
class D: ...
44-
45-
# More circular references
46-
class Leaf: ...
47-
class Tree(list[Tree | Leaf]): ... # Still invalid even when `__future__.annotations` are enabled
48-
class Tree2(list["Tree | Leaf"]): ... # always okay
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
"""Regression test for #10451.
2+
3+
Annotations in a class are allowed to be forward references
4+
if `from __future__ import annotations` is active,
5+
even if they're in a class included in
6+
`lint.flake8-type-checking.runtime-evaluated-base-classes`.
7+
8+
They're not allowed to refer to symbols that cannot be *resolved*
9+
at runtime, however.
10+
"""
11+
12+
from __future__ import annotations
13+
14+
from sqlalchemy.orm import DeclarativeBase, Mapped
15+
16+
17+
class Base(DeclarativeBase):
18+
some_mapping: Mapped[list[Bar]] | None = None # Should not trigger F821 (resolveable forward reference)
19+
simplified: list[Bar] | None = None # Should not trigger F821 (resolveable forward reference)
20+
21+
22+
class Bar:
23+
pass

crates/ruff_linter/src/checkers/ast/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -937,7 +937,6 @@ impl<'a> Visitor<'a> for Checker<'a> {
937937
&& !self.semantic.in_deferred_type_definition()
938938
&& self.semantic.in_type_definition()
939939
&& self.semantic.future_annotations()
940-
&& (self.semantic.in_typing_only_annotation() || self.source_type.is_stub())
941940
{
942941
if let Expr::StringLiteral(ast::ExprStringLiteral { value, .. }) = expr {
943942
self.visit.string_type_definitions.push((

crates/ruff_linter/src/rules/pyflakes/mod.rs

+23
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,29 @@ mod tests {
180180
Ok(())
181181
}
182182

183+
#[test_case(Rule::UndefinedName, Path::new("F821_29.py"))]
184+
fn rules_with_flake8_type_checking_settings_enabled(
185+
rule_code: Rule,
186+
path: &Path,
187+
) -> Result<()> {
188+
let snapshot = format!("{}_{}", rule_code.noqa_code(), path.to_string_lossy());
189+
let diagnostics = test_path(
190+
Path::new("pyflakes").join(path).as_path(),
191+
&LinterSettings {
192+
flake8_type_checking: crate::rules::flake8_type_checking::settings::Settings {
193+
runtime_required_base_classes: vec![
194+
"pydantic.BaseModel".to_string(),
195+
"sqlalchemy.orm.DeclarativeBase".to_string(),
196+
],
197+
..Default::default()
198+
},
199+
..LinterSettings::for_rule(rule_code)
200+
},
201+
)?;
202+
assert_messages!(snapshot, diagnostics);
203+
Ok(())
204+
}
205+
183206
#[test_case(Rule::UnusedVariable, Path::new("F841_4.py"))]
184207
fn preview_rules(rule_code: Rule, path: &Path) -> Result<()> {
185208
let snapshot = format!(

crates/ruff_linter/src/rules/pyflakes/snapshots/ruff_linter__rules__pyflakes__tests__F821_F821_27.py.snap

-27
Original file line numberDiff line numberDiff line change
@@ -17,30 +17,3 @@ F821_27.py:34:8: F821 Undefined name `baz`
1717
| ^^^ F821
1818
35 | eggs = "baz" # always okay
1919
|
20-
21-
F821_27.py:38:33: F821 Undefined name `DStr`
22-
|
23-
37 | # Forward references:
24-
38 | MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled
25-
| ^^^^ F821
26-
39 | MaybeDStr2: TypeAlias = Optional["DStr"] # always okay
27-
40 | DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled
28-
|
29-
30-
F821_27.py:40:25: F821 Undefined name `D`
31-
|
32-
38 | MaybeDStr: TypeAlias = Optional[DStr] # Still invalid even when `__future__.annotations` are enabled
33-
39 | MaybeDStr2: TypeAlias = Optional["DStr"] # always okay
34-
40 | DStr: TypeAlias = Union[D, str] # Still invalid even when `__future__.annotations` are enabled
35-
| ^ F821
36-
41 | DStr2: TypeAlias = Union["D", str] # always okay
37-
|
38-
39-
F821_27.py:47:17: F821 Undefined name `Tree`
40-
|
41-
45 | # More circular references
42-
46 | class Leaf: ...
43-
47 | class Tree(list[Tree | Leaf]): ... # Still invalid even when `__future__.annotations` are enabled
44-
| ^^^^ F821
45-
48 | class Tree2(list["Tree | Leaf"]): ... # always okay
46-
|
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pyflakes/mod.rs
3+
---
4+

0 commit comments

Comments
 (0)