From 46d4da345d4cd4ef6a43199dffa2901c6ec7f4b2 Mon Sep 17 00:00:00 2001 From: alliasgher Date: Sun, 9 Aug 2026 02:16:35 +0500 Subject: [PATCH 1/2] Preserve blank lines before a closing fmt: on comment The blank run that terminates a fmt: off region lives in the prefix of the following '# fmt: on' standalone comment, not in the verbatim block that _handle_regular_fmt_block produces. _maybe_empty_lines therefore capped it to max_allowed like any ordinary blank run, silently reformatting lines the user had opted out of. Skip the cap for a '# fmt: on' comment that closes a converted block. --- src/black/lines.py | 12 ++++++++++- tests/data/cases/fmtonoff9.py | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 tests/data/cases/fmtonoff9.py diff --git a/src/black/lines.py b/src/black/lines.py index cab60fbcf9b..d2628fb468c 100644 --- a/src/black/lines.py +++ b/src/black/lines.py @@ -5,6 +5,7 @@ from typing import NamedTuple, Optional, TypeVar, Union, cast from black.brackets import COMMA_PRIORITY, DOT_PRIORITY, BracketTracker +from black.comments import FMT_ON, contains_fmt_directive from black.mode import Mode, Preview from black.nodes import ( BRACKETS, @@ -1009,7 +1010,16 @@ def _maybe_empty_lines(self, current_line: Line) -> tuple[int, int]: # Consume the first leaf's extra newlines. first_leaf = current_line.leaves[0] before = first_leaf.prefix.count("\n") - before = min(before, max_allowed) + # The blank lines that terminate a `# fmt: off` region live in the + # prefix of the `# fmt: on` comment, not in the verbatim block, so + # capping them here would edit formatting that was opted out of. + if not ( + first_leaf.type == STANDALONE_COMMENT + and contains_fmt_directive(first_leaf.value, FMT_ON) + and self.previous_line is not None + and self.previous_line.is_fmt_pass_converted() + ): + before = min(before, max_allowed) first_leaf.prefix = "" else: before = 0 diff --git a/tests/data/cases/fmtonoff9.py b/tests/data/cases/fmtonoff9.py new file mode 100644 index 00000000000..aeb43e68841 --- /dev/null +++ b/tests/data/cases/fmtonoff9.py @@ -0,0 +1,40 @@ +# Regression test for https://github.com/psf/black/issues/2877. +# Blank lines that terminate a `# fmt: off` region are inside the region, so +# they must be preserved rather than collapsed to the usual maximum. +x = 1 +# fmt: off +a = 1 + + + +# fmt: on +y = 2 + + +def f(): + x = 1 + # fmt: off + a = 1 + + + + + # fmt: on + y = 2 + + +# yapf: disable +b = 1 + + + +# yapf: enable +z = 2 + + +# fmt:off +c = 1 + + +# fmt:on +w = 2 From 6ca34c38c7706bf74bf408bb8ba12f086dc1fe45 Mon Sep 17 00:00:00 2001 From: alliasgher Date: Sun, 9 Aug 2026 02:17:09 +0500 Subject: [PATCH 2/2] Add CHANGES.md entry --- CHANGES.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 14fcc56680f..1290e8ee1d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -17,6 +17,10 @@ +- Preserve blank lines that come immediately before a `# fmt: on` comment. They are + inside the opted-out region, but they live in the prefix of the `# fmt: on` comment + rather than in the verbatim block, so they were being capped like any ordinary blank + run (#5300) - Stop treating a t-string in docstring position as a docstring (for example `t" spam "` as the first statement of a module, class or function). t-strings evaluate to `Template`, never `str`, so stripping and reindenting one changed the