Implement isort's default-section setting (#10149)

## Summary

This fixes https://github.com/astral-sh/ruff/issues/7868.

Support isort's `default-section` feature which allows any imports that
match sections that are not in `section-order` to be mapped to a
specifically named section.


https://pycqa.github.io/isort/docs/configuration/options.html#default-section

This has a few implications:

- It is no longer required that all known sections are defined in
`section-order`.
- This is technically a bw-incompat change because currently if folks
define custom groups, and do not define a `section-order`, the code used
to add all known sections to `section-order` while emitting warnings.
**However, when this happened, users would be seeing warnings so I do
not think it should count as a bw-incompat change.**

## Test Plan

- Added a new test.
- Did not break any existing tests.

Finally, I ran the following config against Pyramid's complex codebase
that was previously using isort and this change worked there.

### pyramid's previous isort config


5f7e286b06/pyproject.toml (L22-L37)

```toml
[tool.isort]
profile = "black"
multi_line_output = 3
src_paths = ["src", "tests"]
skip_glob = ["docs/*"]
include_trailing_comma = true
force_grid_wrap = false
combine_as_imports = true
line_length = 79
force_sort_within_sections = true
no_lines_before = "THIRDPARTY"
sections = "FUTURE,THIRDPARTY,FIRSTPARTY,LOCALFOLDER"
default_section = "THIRDPARTY"
known_first_party = "pyramid"
```

### tested with ruff isort config

```toml
[tool.ruff.lint.isort]
case-sensitive = true
combine-as-imports = true
force-sort-within-sections = true
section-order = [
    "future",
    "third-party",
    "first-party",
    "local-folder",
]
default-section = "third-party"
known-first-party = [
    "pyramid",
]
```
This commit is contained in:
Michael Merickel 2024-02-29 20:32:03 -07:00 committed by GitHub
parent 8e0a70cfa3
commit c9931a548f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 174 additions and 23 deletions

View file

@ -2099,6 +2099,16 @@ pub struct IsortOptions {
)]
pub section_order: Option<Vec<ImportSection>>,
/// Define a default section for any imports that don't fit into the specified `section-order`.
#[option(
default = r#"third-party"#,
value_type = "str",
example = r#"
default-section = "third-party"
"#
)]
pub default_section: Option<ImportSection>,
/// Put all imports into the same section bucket.
///
/// For example, rather than separating standard library and third-party imports, as in:
@ -2226,6 +2236,9 @@ impl IsortOptions {
if no_sections && self.section_order.is_some() {
warn_user_once!("`section-order` is ignored when `no-sections` is set to `true`");
}
if no_sections && self.default_section.is_some() {
warn_user_once!("`default-section` is ignored when `no-sections` is set to `true`");
}
if no_sections && self.sections.is_some() {
warn_user_once!("`sections` is ignored when `no-sections` is set to `true`");
}
@ -2241,6 +2254,10 @@ impl IsortOptions {
let mut section_order: Vec<_> = self
.section_order
.unwrap_or_else(|| ImportType::iter().map(ImportSection::Known).collect());
let default_section = self
.default_section
.unwrap_or(ImportSection::Known(ImportType::ThirdParty));
let known_first_party = self
.known_first_party
.map(|names| {
@ -2344,24 +2361,13 @@ impl IsortOptions {
}
}
// Add all built-in sections to `section_order`, if not already present.
for section in ImportType::iter().map(ImportSection::Known) {
if !section_order.contains(&section) {
warn_user_once!(
"`section-order` is missing built-in section: `{:?}`",
section
);
section_order.push(section);
}
}
// Add all user-defined sections to `section-order`, if not already present.
for section_name in sections.keys() {
let section = ImportSection::UserDefined(section_name.clone());
if !section_order.contains(&section) {
warn_user_once!("`section-order` is missing section: `{:?}`", section);
section_order.push(section);
}
// Verify that `default_section` is in `section_order`.
if !section_order.contains(&default_section) {
warn_user_once!(
"`section-order` must contain `default-section`: {:?}",
default_section,
);
section_order.push(default_section.clone());
}
Ok(isort::settings::Settings {
@ -2394,6 +2400,7 @@ impl IsortOptions {
lines_between_types,
forced_separate: Vec::from_iter(self.forced_separate.unwrap_or_default()),
section_order,
default_section,
no_sections,
from_first,
length_sort: self.length_sort.unwrap_or(false),