Use single newlines in .pyi import sorting (#1142)

This commit is contained in:
Charlie Marsh 2022-12-08 11:34:41 -05:00 committed by GitHub
parent e338d9acbe
commit 49df43bb78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 148 additions and 25 deletions

View file

@ -0,0 +1,41 @@
import a
import b
x = 1
import os
import sys
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
y = 1
import os
import sys
"""Docstring"""
if True:
import os
def f():
pass
if True:
import os
def f():
pass
if True:
x = 1
import collections
import typing
class X: pass
if True:
x = 1
import collections
import typing
def f(): pass

View file

@ -1,5 +1,7 @@
//! Lint rules based on import analysis. //! Lint rules based on import analysis.
use std::path::Path;
use rustpython_parser::ast::Suite; use rustpython_parser::ast::Suite;
use crate::ast::visitor::Visitor; use crate::ast::visitor::Visitor;
@ -33,8 +35,9 @@ pub fn check_imports(
directives: &IsortDirectives, directives: &IsortDirectives,
settings: &Settings, settings: &Settings,
autofix: bool, autofix: bool,
path: &Path,
) -> Vec<Check> { ) -> Vec<Check> {
let mut tracker = ImportTracker::new(directives); let mut tracker = ImportTracker::new(directives, path);
for stmt in python_ast { for stmt in python_ast {
tracker.visit_stmt(stmt); tracker.visit_stmt(stmt);
} }

View file

@ -559,6 +559,7 @@ mod tests {
#[test_case(Path::new("force_wrap_aliases.py"))] #[test_case(Path::new("force_wrap_aliases.py"))]
#[test_case(Path::new("import_from_after_import.py"))] #[test_case(Path::new("import_from_after_import.py"))]
#[test_case(Path::new("insert_empty_lines.py"))] #[test_case(Path::new("insert_empty_lines.py"))]
#[test_case(Path::new("insert_empty_lines.pyi"))]
#[test_case(Path::new("leading_prefix.py"))] #[test_case(Path::new("leading_prefix.py"))]
#[test_case(Path::new("no_reorder_within_section.py"))] #[test_case(Path::new("no_reorder_within_section.py"))]
#[test_case(Path::new("no_wrap_star.py"))] #[test_case(Path::new("no_wrap_star.py"))]

View file

@ -0,0 +1,65 @@
---
source: src/isort/mod.rs
expression: checks
---
- kind: UnsortedImports
location:
row: 1
column: 0
end_location:
row: 3
column: 0
fix:
content: "import a\nimport b\n\n"
location:
row: 1
column: 0
end_location:
row: 3
column: 0
- kind: UnsortedImports
location:
row: 4
column: 0
end_location:
row: 6
column: 0
fix:
content: "import os\nimport sys\n\n"
location:
row: 4
column: 0
end_location:
row: 6
column: 0
- kind: UnsortedImports
location:
row: 14
column: 0
end_location:
row: 16
column: 0
fix:
content: "import os\nimport sys\n\n"
location:
row: 14
column: 0
end_location:
row: 16
column: 0
- kind: UnsortedImports
location:
row: 33
column: 0
end_location:
row: 35
column: 0
fix:
content: " import collections\n import typing\n\n"
location:
row: 33
column: 0
end_location:
row: 35
column: 0

View file

@ -1,3 +1,5 @@
use std::path::Path;
use rustpython_ast::{ use rustpython_ast::{
Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler, Alias, Arg, Arguments, Boolop, Cmpop, Comprehension, Constant, Excepthandler,
ExcepthandlerKind, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, StmtKind, ExcepthandlerKind, Expr, ExprContext, Keyword, MatchCase, Operator, Pattern, Stmt, StmtKind,
@ -20,16 +22,18 @@ pub struct Block<'a> {
} }
pub struct ImportTracker<'a> { pub struct ImportTracker<'a> {
blocks: Vec<Block<'a>>,
directives: &'a IsortDirectives, directives: &'a IsortDirectives,
pyi: bool,
blocks: Vec<Block<'a>>,
split_index: usize, split_index: usize,
nested: bool, nested: bool,
} }
impl<'a> ImportTracker<'a> { impl<'a> ImportTracker<'a> {
pub fn new(directives: &'a IsortDirectives) -> Self { pub fn new(directives: &'a IsortDirectives, path: &'a Path) -> Self {
Self { Self {
directives, directives,
pyi: path.extension().map_or(false, |ext| ext == "pyi"),
blocks: vec![Block::default()], blocks: vec![Block::default()],
split_index: 0, split_index: 0,
nested: false, nested: false,
@ -41,6 +45,34 @@ impl<'a> ImportTracker<'a> {
self.blocks[index].imports.push(stmt); self.blocks[index].imports.push(stmt);
} }
fn trailer_for(&self, stmt: &'a Stmt) -> Option<Trailer> {
if self.pyi {
// Black treats interface files differently, limiting to one newline
// (`Trailing::Sibling`), and avoiding inserting any newlines in nested function
// blocks.
if self.nested
&& matches!(
stmt.node,
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
)
{
None
} else {
Some(Trailer::Sibling)
}
} else if self.nested {
Some(Trailer::Sibling)
} else {
Some(match &stmt.node {
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
Trailer::FunctionDef
}
StmtKind::ClassDef { .. } => Trailer::ClassDef,
_ => Trailer::Sibling,
})
}
}
fn finalize(&mut self, trailer: Option<Trailer>) { fn finalize(&mut self, trailer: Option<Trailer>) {
let index = self.blocks.len() - 1; let index = self.blocks.len() - 1;
if !self.blocks[index].imports.is_empty() { if !self.blocks[index].imports.is_empty() {
@ -62,17 +94,7 @@ where
// Track manual splits. // Track manual splits.
while self.split_index < self.directives.splits.len() { while self.split_index < self.directives.splits.len() {
if stmt.location.row() >= self.directives.splits[self.split_index] { if stmt.location.row() >= self.directives.splits[self.split_index] {
self.finalize(Some(if self.nested { self.finalize(self.trailer_for(stmt));
Trailer::Sibling
} else {
match &stmt.node {
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
Trailer::FunctionDef
}
StmtKind::ClassDef { .. } => Trailer::ClassDef,
_ => Trailer::Sibling,
}
}));
self.split_index += 1; self.split_index += 1;
} else { } else {
break; break;
@ -87,17 +109,7 @@ where
{ {
self.track_import(stmt); self.track_import(stmt);
} else { } else {
self.finalize(Some(if self.nested { self.finalize(self.trailer_for(stmt));
Trailer::Sibling
} else {
match &stmt.node {
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
Trailer::FunctionDef
}
StmtKind::ClassDef { .. } => Trailer::ClassDef,
_ => Trailer::Sibling,
}
}));
} }
// Track scope. // Track scope.

View file

@ -92,6 +92,7 @@ pub(crate) fn check_path(
&directives.isort, &directives.isort,
settings, settings,
autofix, autofix,
path,
)); ));
} }
} }