Skip to content

Commit

Permalink
Don't treat straight imports of __future__ as __future__ imports (#…
Browse files Browse the repository at this point in the history
…5128)

## Summary

If you `import __future__`, it's not subject to the same rules as `from
__future__ import feature` -- i.e., this is fine:

```python
x = 1

import __future__
```

It doesn't really make sense to treat these as `__future__` imports
(though I can't imagine anyone ever does this anyway).
  • Loading branch information
charliermarsh committed Jun 15, 2023
1 parent 1e38348 commit 1f856aa
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 26 deletions.
11 changes: 11 additions & 0 deletions crates/ruff/resources/test/fixtures/pyflakes/F401_18.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""Test that straight `__future__` imports are considered unused."""


def f():
import __future__


def f():
import __future__

print(__future__.absolute_import)
19 changes: 1 addition & 18 deletions crates/ruff/src/checkers/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,24 +805,7 @@ where
}

for alias in names {
if &alias.name == "__future__" {
let name = alias.asname.as_ref().unwrap_or(&alias.name);
self.add_binding(
name,
alias.identifier(self.locator),
BindingKind::FutureImportation,
BindingFlags::empty(),
);

if self.enabled(Rule::LateFutureImport) {
if self.semantic.seen_futures_boundary() {
self.diagnostics.push(Diagnostic::new(
pyflakes::rules::LateFutureImport,
stmt.range(),
));
}
}
} else if alias.name.contains('.') && alias.asname.is_none() {
if alias.name.contains('.') && alias.asname.is_none() {
// Given `import foo.bar`, `name` would be "foo", and `qualified_name` would be
// "foo.bar".
let name = alias.name.split('.').next().unwrap();
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/rules/pyflakes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ mod tests {
#[test_case(Rule::UnusedImport, Path::new("F401_15.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_16.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_17.py"))]
#[test_case(Rule::UnusedImport, Path::new("F401_18.py"))]
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"))]
#[test_case(Rule::UndefinedLocalWithImportStar, Path::new("F403.py"))]
#[test_case(Rule::LateFutureImport, Path::new("F404.py"))]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
---
source: crates/ruff/src/rules/pyflakes/mod.rs
---
F401_18.py:5:12: F401 [*] `__future__` imported but unused
|
4 | def f():
5 | import __future__
| ^^^^^^^^^^ F401
|
= help: Remove unused import: `future`

Fix
2 2 |
3 3 |
4 4 | def f():
5 |- import __future__
5 |+ pass
6 6 |
7 7 |
8 8 | def f():


Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,4 @@ F404.py:6:1: F404 `from __future__` imports must occur at the beginning of the f
8 | import __future__
|

F404.py:8:1: F404 `from __future__` imports must occur at the beginning of the file
|
6 | from __future__ import print_function
7 |
8 | import __future__
| ^^^^^^^^^^^^^^^^^ F404
|


0 comments on commit 1f856aa

Please sign in to comment.