Skip to content

Commit

Permalink
implemented option lines-between-types for isort (#2762)
Browse files Browse the repository at this point in the history
Fixes #2585

Add support for the isort option [lines_between_types](https://pycqa.github.io/isort/docs/configuration/options.html#lines-between-types)
  • Loading branch information
PushUpek committed Feb 11, 2023
1 parent 70ff651 commit 77099dc
Show file tree
Hide file tree
Showing 8 changed files with 140 additions and 8 deletions.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3480,8 +3480,7 @@ known-first-party = ["src"]
#### [`known-local-folder`](#known-local-folder)

A list of modules to consider being a local folder.
Generally, this is reserved for relative
imports (from . import module).
Generally, this is reserved for relative imports (`from . import module`).

**Default value**: `[]`

Expand Down Expand Up @@ -3517,7 +3516,7 @@ known-third-party = ["src"]
#### [`lines-after-imports`](#lines-after-imports)

The number of blank lines to place after imports.
-1 for automatic determination.
Use `-1` for automatic determination.

**Default value**: `-1`

Expand All @@ -3533,6 +3532,24 @@ lines-after-imports = 1

---

#### [`lines-between-types`](#lines-between-types)

The number of lines to place between "direct" and `import from` imports.

**Default value**: `0`

**Type**: `int`

**Example usage**:

```toml
[tool.ruff.isort]
# Use a single line between direct and from import
lines-between-types = 1
```

---

#### [`no-lines-before`](#no-lines-before)

A list of sections that should _not_ be delineated from the previous
Expand Down
16 changes: 16 additions & 0 deletions crates/ruff/resources/test/fixtures/isort/lines_between_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from __future__ import annotations

import datetime
import json


from binascii import hexlify

import requests


from sanic import Sanic
from loguru import Logger

from . import config
from .data import Data
1 change: 1 addition & 0 deletions crates/ruff/resources/test/fixtures/isort/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ line-length = 88

[tool.ruff.isort]
lines-after-imports = 3
lines-between-types = 2
known-local-folder = ["ruff"]
36 changes: 36 additions & 0 deletions crates/ruff/src/rules/isort/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ pub fn format_imports(
variables: &BTreeSet<String>,
no_lines_before: &BTreeSet<ImportType>,
lines_after_imports: isize,
lines_between_types: usize,
forced_separate: &[String],
target_version: PythonVersion,
) -> String {
Expand Down Expand Up @@ -165,6 +166,7 @@ pub fn format_imports(
constants,
variables,
no_lines_before,
lines_between_types,
target_version,
);

Expand Down Expand Up @@ -223,6 +225,7 @@ fn format_import_block(
constants: &BTreeSet<String>,
variables: &BTreeSet<String>,
no_lines_before: &BTreeSet<ImportType>,
lines_between_types: usize,
target_version: PythonVersion,
) -> String {
// Categorize by type (e.g., first-party vs. third-party).
Expand Down Expand Up @@ -277,6 +280,8 @@ fn format_import_block(
output.push_str(stylist.line_ending());
}

let mut lines_inserted = false;
let mut has_direct_import = false;
let mut is_first_statement = true;
for import in imports {
match import {
Expand All @@ -287,8 +292,20 @@ fn format_import_block(
is_first_statement,
stylist,
));

has_direct_import = true;
}

ImportFrom((import_from, comments, trailing_comma, aliases)) => {
// Add a blank lines between direct and from imports
if lines_between_types > 0 && has_direct_import && !lines_inserted {
for _ in 0..lines_between_types {
output.push_str(stylist.line_ending());
}

lines_inserted = true;
}

output.push_str(&format::format_import_from(
&import_from,
&comments,
Expand Down Expand Up @@ -752,6 +769,25 @@ mod tests {
Ok(())
}

#[test_case(Path::new("lines_between_types.py"))]
fn lines_between_types(path: &Path) -> Result<()> {
let snapshot = format!("lines_between_types{}", path.to_string_lossy());
let mut diagnostics = test_path(
Path::new("isort").join(path).as_path(),
&Settings {
isort: super::settings::Settings {
lines_between_types: 2,
..super::settings::Settings::default()
},
src: vec![test_resource_path("fixtures/isort")],
..Settings::for_rule(Rule::UnsortedImports)
},
)?;
diagnostics.sort_by_key(|diagnostic| diagnostic.location);
assert_yaml_snapshot!(snapshot, diagnostics);
Ok(())
}

#[test_case(Path::new("forced_separate.py"))]
fn forced_separate(path: &Path) -> Result<()> {
let snapshot = format!("{}", path.to_string_lossy());
Expand Down
1 change: 1 addition & 0 deletions crates/ruff/src/rules/isort/rules/organize_imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ pub fn organize_imports(
&settings.isort.variables,
&settings.isort.no_lines_before,
settings.isort.lines_after_imports,
settings.isort.lines_between_types,
&settings.isort.forced_separate,
settings.target_version,
);
Expand Down
19 changes: 16 additions & 3 deletions crates/ruff/src/rules/isort/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,7 @@ pub struct Options {
"#
)]
/// A list of modules to consider being a local folder.
/// Generally, this is reserved for relative
/// imports (from . import module).
/// Generally, this is reserved for relative imports (`from . import module`).
pub known_local_folder: Option<Vec<String>>,
#[option(
default = r#"[]"#,
Expand Down Expand Up @@ -233,8 +232,18 @@ pub struct Options {
"#
)]
/// The number of blank lines to place after imports.
/// -1 for automatic determination.
/// Use `-1` for automatic determination.
pub lines_after_imports: Option<isize>,
#[option(
default = r#"0"#,
value_type = "int",
example = r#"
# Use a single line between direct and from import
lines-between-types = 1
"#
)]
/// The number of lines to place between "direct" and `import from` imports.
pub lines_between_types: Option<usize>,
#[option(
default = r#"[]"#,
value_type = "Vec<String>",
Expand Down Expand Up @@ -268,6 +277,7 @@ pub struct Settings {
pub variables: BTreeSet<String>,
pub no_lines_before: BTreeSet<ImportType>,
pub lines_after_imports: isize,
pub lines_between_types: usize,
pub forced_separate: Vec<String>,
}

Expand All @@ -292,6 +302,7 @@ impl Default for Settings {
variables: BTreeSet::new(),
no_lines_before: BTreeSet::new(),
lines_after_imports: -1,
lines_between_types: 0,
forced_separate: Vec::new(),
}
}
Expand Down Expand Up @@ -322,6 +333,7 @@ impl From<Options> for Settings {
variables: BTreeSet::from_iter(options.variables.unwrap_or_default()),
no_lines_before: BTreeSet::from_iter(options.no_lines_before.unwrap_or_default()),
lines_after_imports: options.lines_after_imports.unwrap_or(-1),
lines_between_types: options.lines_between_types.unwrap_or_default(),
forced_separate: Vec::from_iter(options.forced_separate.unwrap_or_default()),
}
}
Expand All @@ -348,6 +360,7 @@ impl From<Settings> for Options {
variables: Some(settings.variables.into_iter().collect()),
no_lines_before: Some(settings.no_lines_before.into_iter().collect()),
lines_after_imports: Some(settings.lines_after_imports),
lines_between_types: Some(settings.lines_between_types),
forced_separate: Some(settings.forced_separate.into_iter().collect()),
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
source: crates/ruff/src/rules/isort/mod.rs
expression: diagnostics
---
- kind:
UnsortedImports: ~
location:
row: 1
column: 0
end_location:
row: 17
column: 0
fix:
content:
- from __future__ import annotations
- ""
- import datetime
- import json
- ""
- ""
- from binascii import hexlify
- ""
- import requests
- ""
- ""
- from loguru import Logger
- from sanic import Sanic
- ""
- from . import config
- from .data import Data
- ""
location:
row: 1
column: 0
end_location:
row: 17
column: 0
parent: ~

13 changes: 11 additions & 2 deletions ruff.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -965,7 +965,7 @@
}
},
"known-local-folder": {
"description": "A list of modules to consider being a local folder. Generally, this is reserved for relative imports (from . import module).",
"description": "A list of modules to consider being a local folder. Generally, this is reserved for relative imports (`from . import module`).",
"type": [
"array",
"null"
Expand All @@ -985,13 +985,22 @@
}
},
"lines-after-imports": {
"description": "The number of blank lines to place after imports. -1 for automatic determination.",
"description": "The number of blank lines to place after imports. Use `-1` for automatic determination.",
"type": [
"integer",
"null"
],
"format": "int"
},
"lines-between-types": {
"description": "The number of lines to place between \"direct\" and `import from` imports.",
"type": [
"integer",
"null"
],
"format": "uint",
"minimum": 0.0
},
"no-lines-before": {
"description": "A list of sections that should _not_ be delineated from the previous section via empty lines.",
"type": [
Expand Down

0 comments on commit 77099dc

Please sign in to comment.