From 3f5a91cecfd113d9e4a8f40f960293457adc8db0 Mon Sep 17 00:00:00 2001 From: Brent Westbrook <36778786+ntBre@users.noreply.github.com> Date: Wed, 23 Sep 2026 13:25:37 -0400 Subject: [PATCH] Stabilize pragma comment width handling (#28795) Summary -- I wasn't really planning to do a full stabilization pass, but I quickly skimmed through `preview.rs` and remembered that the isort pragma change was kind a bug fix but technically breaking and thought we might want to stabilize it. This PR stabilizes the isort pragma comment width handling introduced in #27313, along with the related pragma handling for `E501` and `W505` from #24071. There was a comment about also stabilizing the related formatter preview feature on the function from #24071, so that is stabilized here too. I'm fine holding off on this, especially if the formatter change is a better fit for the 2027 style guide. Like I said above, I mostly wanted to stabilize the isort change, but that snowballed a bit since the helpers were shared. Test Plan -- Updated existing snapshots --- .../isort/fit_line_length_merged_pragma.py | 2 +- .../isort/fit_line_length_mixed_pragma.py | 2 +- .../fixtures/isort/fit_line_length_pragma.py | 4 +- crates/ruff_linter/src/line_width.rs | 45 ++--------------- crates/ruff_linter/src/preview.rs | 11 ---- crates/ruff_linter/src/rules/isort/format.rs | 15 ++---- crates/ruff_linter/src/rules/isort/mod.rs | 34 ++----------- .../src/rules/isort/rules/organize_imports.rs | 1 - ..._fit_line_length_merged_pragma_fixed.snap} | 2 +- ...ine_length_merged_pragma_fixed_stable.snap | 23 --------- ...ests__fit_line_length_mixed_pragma.py.snap | 26 ++++------ ...ort__tests__fit_line_length_pragma.py.snap | 41 ++++----------- ...les__isort__tests__inline_comments.py.snap | 30 +++++------ ...view__fit_line_length_mixed_pragma.py.snap | 24 --------- ...s__preview__fit_line_length_pragma.py.snap | 25 ---------- .../ruff_linter/src/rules/pycodestyle/mod.rs | 2 +- .../src/rules/pycodestyle/overlong.rs | 19 +++---- .../pycodestyle/rules/doc_line_too_long.rs | 1 - .../rules/pycodestyle/rules/line_too_long.rs | 1 - ...tyle__tests__line-too-long_E501_5.py.snap} | 0 .../fixtures/ruff/trailing_pragma_nested.py | 6 +-- .../src/comments/format.rs | 22 +++----- crates/ruff_python_formatter/src/preview.rs | 10 ---- .../format@trailing_pragma_nested.py.snap | 50 ++++--------------- docs/formatter/black.md | 2 + 25 files changed, 80 insertions(+), 318 deletions(-) rename crates/ruff_linter/src/rules/isort/snapshots/{ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_preview.snap => ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed.snap} (97%) delete mode 100644 crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_stable.snap delete mode 100644 crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_mixed_pragma.py.snap delete mode 100644 crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_pragma.py.snap rename crates/ruff_linter/src/rules/pycodestyle/snapshots/{ruff_linter__rules__pycodestyle__tests__preview__line-too-long_E501_5.py.snap => ruff_linter__rules__pycodestyle__tests__line-too-long_E501_5.py.snap} (100%) diff --git a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_merged_pragma.py b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_merged_pragma.py index 769e7cc701..0badd6de62 100644 --- a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_merged_pragma.py +++ b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_merged_pragma.py @@ -9,7 +9,7 @@ from aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaa import ( # explain # non-pragma prefix is 86 columns. from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # explain # noqa: TID251 -# Separate comments with the pragma first: when isort collapses this import in preview, +# Separate comments with the pragma first: when isort collapses this import, # the merged comment token is pragma-prefixed, so E501 strips it entirely and the # collapsed 104-column line is fine. from ccccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccc import ( # noqa: TID251 diff --git a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_mixed_pragma.py b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_mixed_pragma.py index d2b0744707..342c34f104 100644 --- a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_mixed_pragma.py +++ b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_mixed_pragma.py @@ -1,5 +1,5 @@ # The next import fits on one line once the trailing pragma is excluded from the width -# (the `# keep this` prefix still counts); in preview it should not be wrapped. +# (the `# keep this` prefix still counts); it should not be wrapped. from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 # The next import exceeds the line length even without the trailing pragma # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. diff --git a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_pragma.py b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_pragma.py index 341bad469d..b041fdaf68 100644 --- a/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_pragma.py +++ b/crates/ruff_linter/resources/test/fixtures/isort/fit_line_length_pragma.py @@ -1,5 +1,5 @@ # The next import fits on one line once the pragma comment is excluded from the width; -# in preview it should not be wrapped (the `# noqa` must stay effective). +# it should not be wrapped (the `# noqa` must stay effective). from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 # The next import exceeds the line length even without the pragma comment; # it must still be wrapped. @@ -8,6 +8,6 @@ from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb def f(): # The next import fits on one line once the pragma comment is excluded from the - # width, so in preview it should not be wrapped. + # width, so it should not be wrapped. from cccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccccccccc import bar # noqa: PLC0415 bar() diff --git a/crates/ruff_linter/src/line_width.rs b/crates/ruff_linter/src/line_width.rs index a04360ef9d..ad159c12f5 100644 --- a/crates/ruff_linter/src/line_width.rs +++ b/crates/ruff_linter/src/line_width.rs @@ -9,35 +9,7 @@ use unicode_width::UnicodeWidthChar; use ruff_cache::{CacheKey, CacheKeyHasher}; use ruff_macros::CacheKey; -use ruff_python_trivia::{find_trailing_pragma_offset, is_pragma_comment, tab_offset}; - -use crate::preview::{ - is_pragma_excluded_from_import_width_enabled, is_trailing_pragma_in_line_length_enabled, -}; -use crate::settings::types::PreviewMode; - -/// Returns the offset within `comment` at which the pragma comment excluded from line-length -/// measurement begins, or `None` if the comment contains no such pragma. -/// -/// This is the shared policy for how pragma comments (e.g., `# noqa: F401` or `# type: ignore`) -/// are excluded when measuring line width, used by `line-too-long` (E501) and -/// `doc-line-too-long` (W505), and, in preview mode, by isort's (I001) decision of whether an -/// import fits on one line (see [`LineWidthBuilder::add_comment`]). The formatter applies the -/// equivalent policy when measuring comment widths. -/// -/// In stable mode, only comments that are pragmas in their entirety are excluded (the returned -/// offset is `0`). In preview mode, a trailing pragma within a mixed comment (e.g., -/// `# explanation # noqa: F401`) is also excluded, in which case the offset points at the `#` -/// that begins the pragma. -pub(crate) fn pragma_offset_for_line_length(comment: &str, preview: PreviewMode) -> Option { - if is_trailing_pragma_in_line_length_enabled(preview) { - find_trailing_pragma_offset(comment) - } else if is_pragma_comment(comment) { - Some(0) - } else { - None - } -} +use ruff_python_trivia::{find_trailing_pragma_offset, tab_offset}; /// The length of a line of text that is considered too long. /// @@ -267,8 +239,8 @@ impl LineWidthBuilder { } /// Adds the width of a trailing comment, including the standard two-space separator that - /// precedes it. In preview mode, any pragma comment is excluded per - /// [`pragma_offset_for_line_length`]. + /// precedes it. Pragma suffixes are excluded per [`find_trailing_pragma_offset`], while + /// any preceding non-pragma comment text still contributes to the width. /// /// Pragma comments are excluded so that adding one to a line never affects whether the line /// is considered to fit, consistent with how `line-too-long` (E501) measures lines. For @@ -281,16 +253,9 @@ impl LineWidthBuilder { /// member, # noqa: PLC0415 /// ) /// ``` - /// - /// Unlike E501, which has always stripped whole-pragma comments on stable, the exclusion - /// changes how imports are formatted, so it is preview-gated in its entirety: on stable, the - /// full comment width is counted. #[must_use] - pub(crate) fn add_comment(self, comment: &str, preview: PreviewMode) -> Self { - if !is_pragma_excluded_from_import_width_enabled(preview) { - return self.add_width(2).add_str(comment); - } - let counted = match pragma_offset_for_line_length(comment, preview) { + pub(crate) fn add_comment(self, comment: &str) -> Self { + let counted = match find_trailing_pragma_offset(comment) { Some(offset) => comment[..offset].trim_end(), None => comment, }; diff --git a/crates/ruff_linter/src/preview.rs b/crates/ruff_linter/src/preview.rs index ca7c51d450..bef07a1564 100644 --- a/crates/ruff_linter/src/preview.rs +++ b/crates/ruff_linter/src/preview.rs @@ -329,12 +329,6 @@ pub(crate) const fn is_up006_future_annotations_fix_enabled(settings: &LinterSet settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/24071 -// Make sure to stabilize the corresponding formatter preview behavior when stabilizing this preview style. -pub(crate) const fn is_trailing_pragma_in_line_length_enabled(preview: PreviewMode) -> bool { - preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/24371 pub(crate) const fn is_collapsible_if_fix_safe_enabled(settings: &LinterSettings) -> bool { settings.preview.is_enabled() @@ -362,11 +356,6 @@ pub(crate) const fn is_b005_precise_diagnostic_enabled(settings: &LinterSettings settings.preview.is_enabled() } -// https://github.com/astral-sh/ruff/pull/27313 -pub(crate) const fn is_pragma_excluded_from_import_width_enabled(preview: PreviewMode) -> bool { - preview.is_enabled() -} - // https://github.com/astral-sh/ruff/pull/27666 pub(crate) const fn is_rule_categories_enabled(preview: PreviewMode) -> bool { preview.is_enabled() diff --git a/crates/ruff_linter/src/rules/isort/format.rs b/crates/ruff_linter/src/rules/isort/format.rs index 90cdc57238..959c87a2dc 100644 --- a/crates/ruff_linter/src/rules/isort/format.rs +++ b/crates/ruff_linter/src/rules/isort/format.rs @@ -1,7 +1,6 @@ use ruff_python_codegen::Stylist; use crate::line_width::{LineLength, LineWidthBuilder}; -use crate::settings::types::PreviewMode; use super::types::{AliasData, ImportCommentSet, ImportFromCommentSet, ImportFromData, Importable}; @@ -55,7 +54,6 @@ pub(crate) fn format_import_from( force_wrap_aliases: bool, is_first: bool, trailing_comma: bool, - preview: PreviewMode, ) -> String { if aliases.len() == 1 && aliases @@ -69,7 +67,6 @@ pub(crate) fn format_import_from( is_first, stylist, indentation_width, - preview, ); return single_line; } @@ -98,7 +95,6 @@ pub(crate) fn format_import_from( is_first, stylist, indentation_width, - preview, ); if import_width <= line_length || aliases.iter().any(|(alias, _)| alias.name == "*") { return single_line; @@ -118,7 +114,6 @@ fn format_single_line( is_first: bool, stylist: &Stylist, indentation_width: LineWidthBuilder, - preview: PreviewMode, ) -> (String, LineWidthBuilder) { let mut output = String::with_capacity(CAPACITY); let mut line_width = indentation_width; @@ -161,7 +156,7 @@ fn format_single_line( output.push(' '); output.push(' '); output.push_str(comment); - line_width = line_width.add_comment(comment, preview); + line_width = line_width.add_comment(comment); } for (_, comments) in aliases { @@ -169,21 +164,21 @@ fn format_single_line( output.push(' '); output.push(' '); output.push_str(comment); - line_width = line_width.add_comment(comment, preview); + line_width = line_width.add_comment(comment); } for comment in &comments.inline { output.push(' '); output.push(' '); output.push_str(comment); - line_width = line_width.add_comment(comment, preview); + line_width = line_width.add_comment(comment); } for comment in &comments.trailing { output.push(' '); output.push(' '); output.push_str(comment); - line_width = line_width.add_comment(comment, preview); + line_width = line_width.add_comment(comment); } } @@ -191,7 +186,7 @@ fn format_single_line( output.push(' '); output.push(' '); output.push_str(comment); - line_width = line_width.add_comment(comment, preview); + line_width = line_width.add_comment(comment); } output.push_str(&stylist.line_ending()); diff --git a/crates/ruff_linter/src/rules/isort/mod.rs b/crates/ruff_linter/src/rules/isort/mod.rs index 062cfe94e5..78f94d4405 100644 --- a/crates/ruff_linter/src/rules/isort/mod.rs +++ b/crates/ruff_linter/src/rules/isort/mod.rs @@ -20,7 +20,6 @@ use types::{AliasData, ImportBlock, TrailingComma}; use crate::Locator; use crate::line_width::{LineLength, LineWidthBuilder}; use crate::package::PackageRoot; -use crate::settings::types::PreviewMode; use ruff_python_ast::PythonVersion; mod annotate; @@ -78,7 +77,6 @@ pub(crate) fn format_imports( source_type: PySourceType, target_version: PythonVersion, settings: &Settings, - preview: PreviewMode, tokens: &Tokens, ) -> String { let trailer = &block.trailer; @@ -106,7 +104,6 @@ pub(crate) fn format_imports( package, target_version, settings, - preview, ); if !block_output.is_empty() && !output.is_empty() { @@ -163,7 +160,6 @@ fn format_import_block( package: Option>, target_version: PythonVersion, settings: &Settings, - preview: PreviewMode, ) -> String { #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum LineInsertion { @@ -293,7 +289,6 @@ fn format_import_block( is_first_statement, settings.split_on_trailing_comma && matches!(trailing_comma, TrailingComma::Present), - preview, )); if settings.from_first { @@ -323,7 +318,7 @@ mod tests { use crate::registry::Rule; use crate::rules::isort::categorize::{ImportSection, KnownModules}; use crate::settings::LinterSettings; - use crate::settings::types::{IdentifierPattern, PreviewMode}; + use crate::settings::types::IdentifierPattern; use crate::source_kind::SourceKind; use crate::test::{test_contents, test_path, test_resource_path}; @@ -399,38 +394,19 @@ mod tests { Ok(()) } - #[test_case(Path::new("fit_line_length_mixed_pragma.py"))] - #[test_case(Path::new("fit_line_length_pragma.py"))] - fn preview(path: &Path) -> Result<()> { - let snapshot = format!("preview__{}", path.to_string_lossy()); - let diagnostics = test_path( - Path::new("isort").join(path).as_path(), - &LinterSettings { - preview: PreviewMode::Enabled, - src: vec![test_resource_path("fixtures/isort")], - ..LinterSettings::for_rule(Rule::UnsortedImports) - }, - )?; - assert_diagnostics!(snapshot, diagnostics); - Ok(()) - } - /// Fixing I001 must never leave behind a line that E501 then flags. /// /// isort excludes pragma comments (e.g., `# noqa: TID251`) from its width computation /// per comment, while E501 measures the emitted line's single comment token as a whole. /// When isort merges separate comments onto one line (e.g., a statement-level `# explain` /// and an alias-level `# noqa`), the two computations could disagree; this test verifies - /// that the fix nonetheless converges to output that E501 accepts, in both stable and - /// preview modes. - #[test_case(PreviewMode::Disabled, "stable")] - #[test_case(PreviewMode::Enabled, "preview")] - fn no_line_too_long_after_fix(preview: PreviewMode, label: &str) -> Result<()> { + /// that the fix nonetheless converges to output that E501 accepts. + #[test] + fn no_line_too_long_after_fix() -> Result<()> { let path = test_resource_path("fixtures").join("isort/fit_line_length_merged_pragma.py"); let source_type = SourceType::Python(PySourceType::from(&path)); let source_kind = SourceKind::from_path(&path, source_type)?.expect("valid source"); let settings = LinterSettings { - preview, src: vec![test_resource_path("fixtures/isort")], ..LinterSettings::for_rules([Rule::UnsortedImports, Rule::LineTooLong]) }; @@ -438,7 +414,7 @@ mod tests { // `test_contents` applies fixes to convergence (and panics if they fail to converge). let (_, transformed) = test_contents(&source_kind, &path, &settings); insta::assert_snapshot!( - format!("fit_line_length_merged_pragma_fixed_{label}"), + "fit_line_length_merged_pragma_fixed", transformed.source_code() ); diff --git a/crates/ruff_linter/src/rules/isort/rules/organize_imports.rs b/crates/ruff_linter/src/rules/isort/rules/organize_imports.rs index e0aa2f553e..8215f8d5a3 100644 --- a/crates/ruff_linter/src/rules/isort/rules/organize_imports.rs +++ b/crates/ruff_linter/src/rules/isort/rules/organize_imports.rs @@ -170,7 +170,6 @@ pub(crate) fn organize_imports( source_type, target_version, &settings.isort, - settings.preview, tokens, ); diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_preview.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed.snap similarity index 97% rename from crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_preview.snap rename to crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed.snap index 38ba563037..d78f1e1f44 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_preview.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed.snap @@ -11,7 +11,7 @@ from aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaa import x # explain # noq # non-pragma prefix is 86 columns. from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # explain # noqa: TID251 -# Separate comments with the pragma first: when isort collapses this import in preview, +# Separate comments with the pragma first: when isort collapses this import, # the merged comment token is pragma-prefixed, so E501 strips it entirely and the # collapsed 104-column line is fine. from ccccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccc import x # noqa: TID251 # explain diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_stable.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_stable.snap deleted file mode 100644 index aeff3e15ca..0000000000 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_merged_pragma_fixed_stable.snap +++ /dev/null @@ -1,23 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/isort/mod.rs -expression: transformed.source_code() ---- -# Separate statement-level and alias-level comments that isort merges onto one line -# when collapsing. The merged comment token is 89 columns with the `# explain` prefix -# counted, so the import must not end up on an overlong single line. -from aaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaa import ( # explain - x, # noqa: TID251 -) - -# A single mixed comment on an already-collapsed 102-column line. The code plus the -# non-pragma prefix is 86 columns. -from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( - x, # explain # noqa: TID251 -) - -# Separate comments with the pragma first: when isort collapses this import in preview, -# the merged comment token is pragma-prefixed, so E501 strips it entirely and the -# collapsed 104-column line is fine. -from ccccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccc import ( # noqa: TID251 - x, # explain -) diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_mixed_pragma.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_mixed_pragma.py.snap index a1a4fd29d0..6b7e431775 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_mixed_pragma.py.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_mixed_pragma.py.snap @@ -5,24 +5,20 @@ I001 [*] Import block is un-sorted or un-formatted --> fit_line_length_mixed_pragma.py:3:1 | 1 | # The next import fits on one line once the trailing pragma is excluded from the width -2 | # (the `# keep this` prefix still counts); in preview it should not be wrapped. +2 | # (the `# keep this` prefix still counts); it should not be wrapped. 3 | / from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 4 | | # The next import exceeds the line length even without the trailing pragma 5 | | # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. 6 | | from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # keep this # noqa: TID251 | |____________________________________________________________________________^ help: Organize imports - | -2 | # (the `# keep this` prefix still counts); in preview it should not be wrapped. - - from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 -3 + from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import ( -4 + x, # keep this # noqa: TID251 -5 + ) -6 + -7 | # The next import exceeds the line length even without the trailing pragma -8 | # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. - - from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # keep this # noqa: TID251 -9 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( -10 + x, # keep this # noqa: TID251 -11 + ) - | + | +3 | from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 +4 + +5 | # The next import exceeds the line length even without the trailing pragma +6 | # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. + - from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # keep this # noqa: TID251 +7 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( +8 + x, # keep this # noqa: TID251 +9 + ) + | diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_pragma.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_pragma.py.snap index 258b51f527..90508084eb 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_pragma.py.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__fit_line_length_pragma.py.snap @@ -5,7 +5,7 @@ I001 [*] Import block is un-sorted or un-formatted --> fit_line_length_pragma.py:3:1 | 1 | # The next import fits on one line once the pragma comment is excluded from the width; -2 | # in preview it should not be wrapped (the `# noqa` must stay effective). +2 | # it should not be wrapped (the `# noqa` must stay effective). 3 | / from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 4 | | # The next import exceeds the line length even without the pragma comment; 5 | | # it must still be wrapped. @@ -13,36 +13,13 @@ I001 [*] Import block is un-sorted or un-formatted | |_________________________________________________________________________________________^ help: Organize imports | -2 | # in preview it should not be wrapped (the `# noqa` must stay effective). - - from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 -3 + from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import ( -4 + x, # noqa: TID251 -5 + ) -6 + -7 | # The next import exceeds the line length even without the pragma comment; -8 | # it must still be wrapped. +3 | from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 +4 + +5 | # The next import exceeds the line length even without the pragma comment; +6 | # it must still be wrapped. - from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # noqa: TID251 -9 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( -10 + x, # noqa: TID251 -11 + ) -12 | - | - -I001 [*] Import block is un-sorted or un-formatted - --> fit_line_length_pragma.py:12:5 - | -10 | # The next import fits on one line once the pragma comment is excluded from the -11 | # width, so in preview it should not be wrapped. -12 | from cccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccccccccc import bar # noqa: PLC0415 - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -13 | bar() - | -help: Organize imports - | -11 | # width, so in preview it should not be wrapped. - - from cccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccccccccc import bar # noqa: PLC0415 -12 + from cccccccccccccccccccccccccccccc.ccccccccccccccccccccccccccccccccccccc import ( -13 + bar, # noqa: PLC0415 -14 + ) -15 | bar() +7 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( +8 + x, # noqa: TID251 +9 + ) +10 | | diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__inline_comments.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__inline_comments.py.snap index 1e561c6670..1af553f935 100644 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__inline_comments.py.snap +++ b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__inline_comments.py.snap @@ -17,21 +17,15 @@ I001 [*] Import block is un-sorted or un-formatted 11 | | from d.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL, OTHER_RUNNING_TOTAL # type:ignore[attr-defined] | |______________________________________________________________________________________^ help: Organize imports - | -3 | ) - - -4 | from b.prometheus.metrics import ( -5 | TERMINAL_CURRENTLY_RUNNING_TOTAL, # type:ignore[attr-defined] -6 | ) - - - - from c.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL # type:ignore[attr-defined] - - - - from d.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL, OTHER_RUNNING_TOTAL # type:ignore[attr-defined] -7 + from c.prometheus.metrics import ( -8 + TERMINAL_CURRENTLY_RUNNING_TOTAL, # type:ignore[attr-defined] -9 + ) -10 + from d.prometheus.metrics import ( # type:ignore[attr-defined] -11 + OTHER_RUNNING_TOTAL, -12 + TERMINAL_CURRENTLY_RUNNING_TOTAL, -13 + ) - | + | +3 | ) + - +4 | from b.prometheus.metrics import ( +5 | TERMINAL_CURRENTLY_RUNNING_TOTAL, # type:ignore[attr-defined] +6 | ) + - +7 | from c.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL # type:ignore[attr-defined] + - + - from d.prometheus.metrics import TERMINAL_CURRENTLY_RUNNING_TOTAL, OTHER_RUNNING_TOTAL # type:ignore[attr-defined] +8 + from d.prometheus.metrics import OTHER_RUNNING_TOTAL, TERMINAL_CURRENTLY_RUNNING_TOTAL # type:ignore[attr-defined] + | diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_mixed_pragma.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_mixed_pragma.py.snap deleted file mode 100644 index 19e870e003..0000000000 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_mixed_pragma.py.snap +++ /dev/null @@ -1,24 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/isort/mod.rs ---- -I001 [*] Import block is un-sorted or un-formatted - --> fit_line_length_mixed_pragma.py:3:1 - | -1 | # The next import fits on one line once the trailing pragma is excluded from the width -2 | # (the `# keep this` prefix still counts); in preview it should not be wrapped. -3 | / from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 -4 | | # The next import exceeds the line length even without the trailing pragma -5 | | # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. -6 | | from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # keep this # noqa: TID251 - | |____________________________________________________________________________^ -help: Organize imports - | -3 | from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # keep this # noqa: TID251 -4 + -5 | # The next import exceeds the line length even without the trailing pragma -6 | # (code plus the `# keep this` prefix is 89 columns); it must always be wrapped. - - from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # keep this # noqa: TID251 -7 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( -8 + x, # keep this # noqa: TID251 -9 + ) - | diff --git a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_pragma.py.snap b/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_pragma.py.snap deleted file mode 100644 index 5bc4f7f090..0000000000 --- a/crates/ruff_linter/src/rules/isort/snapshots/ruff_linter__rules__isort__tests__preview__fit_line_length_pragma.py.snap +++ /dev/null @@ -1,25 +0,0 @@ ---- -source: crates/ruff_linter/src/rules/isort/mod.rs ---- -I001 [*] Import block is un-sorted or un-formatted - --> fit_line_length_pragma.py:3:1 - | -1 | # The next import fits on one line once the pragma comment is excluded from the width; -2 | # in preview it should not be wrapped (the `# noqa` must stay effective). -3 | / from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 -4 | | # The next import exceeds the line length even without the pragma comment; -5 | | # it must still be wrapped. -6 | | from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # noqa: TID251 - | |_________________________________________________________________________________________^ -help: Organize imports - | -3 | from aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa import x # noqa: TID251 -4 + -5 | # The next import exceeds the line length even without the pragma comment; -6 | # it must still be wrapped. - - from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import x # noqa: TID251 -7 + from bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb.bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb import ( -8 + x, # noqa: TID251 -9 + ) -10 | - | diff --git a/crates/ruff_linter/src/rules/pycodestyle/mod.rs b/crates/ruff_linter/src/rules/pycodestyle/mod.rs index 785eca607d..8ead50bc96 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/mod.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/mod.rs @@ -38,6 +38,7 @@ mod tests { #[test_case(Rule::LineTooLong, Path::new("E501.py"))] #[test_case(Rule::LineTooLong, Path::new("E501_3.py"))] #[test_case(Rule::LineTooLong, Path::new("E501_4.py"))] + #[test_case(Rule::LineTooLong, Path::new("E501_5.py"))] #[test_case(Rule::MixedSpacesAndTabs, Path::new("E101.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E40.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))] @@ -75,7 +76,6 @@ mod tests { Ok(()) } - #[test_case(Rule::LineTooLong, Path::new("E501_5.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E40.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_0.py"))] #[test_case(Rule::ModuleImportNotAtTopOfFile, Path::new("E402_1.py"))] diff --git a/crates/ruff_linter/src/rules/pycodestyle/overlong.rs b/crates/ruff_linter/src/rules/pycodestyle/overlong.rs index 80092565b1..6c2ef705b2 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/overlong.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/overlong.rs @@ -1,11 +1,10 @@ use std::ops::Deref; -use ruff_python_trivia::CommentRanges; +use ruff_python_trivia::{CommentRanges, find_trailing_pragma_offset}; use ruff_source_file::Line; use ruff_text_size::{TextLen, TextRange}; -use crate::line_width::{IndentWidth, LineLength, LineWidthBuilder, pragma_offset_for_line_length}; -use crate::settings::types::PreviewMode; +use crate::line_width::{IndentWidth, LineLength, LineWidthBuilder}; #[derive(Debug)] pub(super) struct Overlong { @@ -22,7 +21,6 @@ impl Overlong { limit: LineLength, task_tags: &[String], tab_size: IndentWidth, - preview: PreviewMode, ) -> Option { // The maximum width of the line is the number of bytes multiplied by the tab size (the // worst-case scenario is that the line is all tabs). If the maximum width is less than the @@ -39,7 +37,7 @@ impl Overlong { } // Strip trailing comments and re-measure the line, if needed. - let line = StrippedLine::from_line(line, comment_ranges, task_tags, preview); + let line = StrippedLine::from_line(line, comment_ranges, task_tags); let width = match &line { StrippedLine::WithoutPragma(line) => { let width = measure(line.as_str(), tab_size); @@ -118,12 +116,7 @@ enum StrippedLine<'a> { impl<'a> StrippedLine<'a> { /// Strip trailing comments from a [`Line`], if the line ends with a pragma comment (like /// `# type: ignore`) or, if necessary, a task comment (like `# TODO`). - fn from_line( - line: &'a Line<'a>, - comment_ranges: &CommentRanges, - task_tags: &[String], - preview: PreviewMode, - ) -> Self { + fn from_line(line: &'a Line<'a>, comment_ranges: &CommentRanges, task_tags: &[String]) -> Self { let [comment_range] = comment_ranges.comments_in_range(line.range()) else { return Self::Unchanged(line); }; @@ -132,8 +125,8 @@ impl<'a> StrippedLine<'a> { let comment_range = comment_range - line.start(); let comment = &line.as_str()[comment_range]; - // Ex) `# type: ignore` or (in preview) `# some comment # noqa: F401` - if let Some(offset) = pragma_offset_for_line_length(comment, preview) { + // Ex) `# type: ignore` or `# some comment # noqa: F401` + if let Some(offset) = find_trailing_pragma_offset(comment) { // Strip the pragma from the line, preserving any preceding non-pragma comment text. let pragma_start = usize::from(comment_range.start()) + offset; let prefix = line[..pragma_start].trim_end(); diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs index afdca7b7e7..a94f5e8a7d 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/doc_line_too_long.rs @@ -105,7 +105,6 @@ pub(crate) fn doc_line_too_long( &[] }, settings.tab_size, - settings.preview, ) { context.report_diagnostic( DocLineTooLong(overlong.width(), limit.value() as usize), diff --git a/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs b/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs index a2d98f1604..1de0ec54b9 100644 --- a/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs +++ b/crates/ruff_linter/src/rules/pycodestyle/rules/line_too_long.rs @@ -101,7 +101,6 @@ pub(crate) fn line_too_long( &[] }, settings.tab_size, - settings.preview, ) { context.report_diagnostic( LineTooLong(overlong.width(), limit.value() as usize), diff --git a/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__line-too-long_E501_5.py.snap b/crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__line-too-long_E501_5.py.snap similarity index 100% rename from crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__preview__line-too-long_E501_5.py.snap rename to crates/ruff_linter/src/rules/pycodestyle/snapshots/ruff_linter__rules__pycodestyle__tests__line-too-long_E501_5.py.snap diff --git a/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_pragma_nested.py b/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_pragma_nested.py index e24810a7e4..b0f499387e 100644 --- a/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_pragma_nested.py +++ b/crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_pragma_nested.py @@ -1,5 +1,5 @@ # Trailing pragma after another comment - the pragma portion should not count -# toward reserved width in preview mode. +# toward reserved width. # The expression is long enough that the formatter would break it if the full # comment width were reserved, but short enough to fit if only the non-pragma @@ -9,9 +9,9 @@ i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # pyright: ignore i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) ## noqa: F401 -# Plain pragma (no nested comment) - should behave the same in stable and preview +# Plain pragma (no nested comment) - should not reserve any width i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: F401 i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: ignore -# Not a pragma - should reserve full width in both modes +# Not a pragma - should reserve full width i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # not a pragma diff --git a/crates/ruff_python_formatter/src/comments/format.rs b/crates/ruff_python_formatter/src/comments/format.rs index 2fa1e5e2e1..a38fe4cb86 100644 --- a/crates/ruff_python_formatter/src/comments/format.rs +++ b/crates/ruff_python_formatter/src/comments/format.rs @@ -3,15 +3,14 @@ use std::borrow::Cow; use ruff_formatter::{FormatError, FormatOptions, SourceCode, format_args, write}; use ruff_python_ast::{AnyNodeRef, NodeKind, PySourceType}; use ruff_python_trivia::{ - CommentLinePosition, find_trailing_pragma_offset, is_pragma_comment, lines_after, - lines_after_ignoring_trivia, lines_before, + CommentLinePosition, find_trailing_pragma_offset, lines_after, lines_after_ignoring_trivia, + lines_before, }; use ruff_text_size::{Ranged, TextLen, TextRange}; use crate::comments::SourceComment; use crate::context::NodeLevel; use crate::prelude::*; -use crate::preview::is_trailing_pragma_in_comment_width_enabled; use crate::statement::suite::should_insert_blank_line_after_class_in_stub_file; /// Formats the leading comments of a node. @@ -378,18 +377,11 @@ impl Format> for FormatTrailingEndOfLineComment<'_> { let normalized_comment = normalize_comment(self.comment, source)?; - // Don't reserve width for pragma comments. In preview, comments - // containing a trailing pragma (e.g., `# comment # noqa: F401`) only - // reserve width for the non-pragma prefix. - let non_pragma_comment_part = if is_trailing_pragma_in_comment_width_enabled(f.context()) { - match find_trailing_pragma_offset(&normalized_comment) { - Some(offset) => normalized_comment[..offset].trim_end(), - None => &normalized_comment, - } - } else if is_pragma_comment(&normalized_comment) { - "" - } else { - &normalized_comment + // Don't reserve width for pragma comments. Comments containing a trailing pragma + // (e.g., `# comment # noqa: F401`) only reserve width for the non-pragma prefix. + let non_pragma_comment_part = match find_trailing_pragma_offset(&normalized_comment) { + Some(offset) => normalized_comment[..offset].trim_end(), + None => &normalized_comment, }; let reserved_width = if non_pragma_comment_part.is_empty() { diff --git a/crates/ruff_python_formatter/src/preview.rs b/crates/ruff_python_formatter/src/preview.rs index 8a195d5b76..958e1b4cd1 100644 --- a/crates/ruff_python_formatter/src/preview.rs +++ b/crates/ruff_python_formatter/src/preview.rs @@ -20,13 +20,3 @@ pub(crate) const fn is_hug_parens_with_braces_and_square_brackets_enabled( pub(crate) const fn is_fluent_layout_split_first_call_enabled(context: &PyFormatContext) -> bool { context.is_preview() } - -/// Returns `true` if the -/// [trailing pragma width handling in comments](https://github.com/astral-sh/ruff/pull/24071) -/// is enabled. -/// When enabled, comments like `# text # noqa: F401` only reserve width for -/// the non-pragma prefix (`# text`), not the trailing pragma. -/// Make sure to stabilize the corresponding linter preview behavior when stabilizing this preview style. -pub(crate) const fn is_trailing_pragma_in_comment_width_enabled(context: &PyFormatContext) -> bool { - context.is_preview() -} diff --git a/crates/ruff_python_formatter/tests/snapshots/format@trailing_pragma_nested.py.snap b/crates/ruff_python_formatter/tests/snapshots/format@trailing_pragma_nested.py.snap index 05d9669b50..c18d04d6fe 100644 --- a/crates/ruff_python_formatter/tests/snapshots/format@trailing_pragma_nested.py.snap +++ b/crates/ruff_python_formatter/tests/snapshots/format@trailing_pragma_nested.py.snap @@ -5,7 +5,7 @@ input_file: crates/ruff_python_formatter/resources/test/fixtures/ruff/trailing_p ## Input ```python # Trailing pragma after another comment - the pragma portion should not count -# toward reserved width in preview mode. +# toward reserved width. # The expression is long enough that the formatter would break it if the full # comment width were reserved, but short enough to fit if only the non-pragma @@ -15,65 +15,33 @@ i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # pyright: ignore i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) ## noqa: F401 -# Plain pragma (no nested comment) - should behave the same in stable and preview +# Plain pragma (no nested comment) - should not reserve any width i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: F401 i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: ignore -# Not a pragma - should reserve full width in both modes +# Not a pragma - should reserve full width i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # not a pragma ``` ## Output ```python # Trailing pragma after another comment - the pragma portion should not count -# toward reserved width in preview mode. +# toward reserved width. # The expression is long enough that the formatter would break it if the full # comment width were reserved, but short enough to fit if only the non-pragma # prefix is reserved. -i = ( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -) # comment # noqa: F401 -i = ( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -) # comment # type: ignore -i = ( - "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", -) # comment # pyright: ignore +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # noqa: F401 +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # type: ignore +i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # pyright: ignore i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) ## noqa: F401 -# Plain pragma (no nested comment) - should behave the same in stable and preview +# Plain pragma (no nested comment) - should not reserve any width i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # noqa: F401 i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # type: ignore -# Not a pragma - should reserve full width in both modes +# Not a pragma - should reserve full width i = ( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ) # comment # not a pragma ``` - - -## Preview changes -```diff ---- Stable -+++ Preview -@@ -4,15 +4,9 @@ - # The expression is long enough that the formatter would break it if the full - # comment width were reserved, but short enough to fit if only the non-pragma - # prefix is reserved. --i = ( -- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", --) # comment # noqa: F401 --i = ( -- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", --) # comment # type: ignore --i = ( -- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", --) # comment # pyright: ignore -+i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # noqa: F401 -+i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # type: ignore -+i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) # comment # pyright: ignore - i = ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",) ## noqa: F401 - - # Plain pragma (no nested comment) - should behave the same in stable and preview -``` diff --git a/docs/formatter/black.md b/docs/formatter/black.md index b8520cd5e5..7b160a0e07 100644 --- a/docs/formatter/black.md +++ b/docs/formatter/black.md @@ -39,6 +39,8 @@ has already been formatted by Black. ### Pragma comments are ignored when computing line width Pragma comments (`# type`, `# noqa`, `# pyright`, `# pylint`, etc.) are ignored when computing the width of a line. +For mixed comments such as `# explanation # noqa: F401`, only the pragma suffix is ignored; +the preceding comment text still contributes to the line width. This prevents Ruff from moving pragma comments around, thereby modifying their meaning and behavior: See Ruff's [pragma comment handling proposal](https://github.com/astral-sh/ruff/discussions/6670)