-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Description
2023: #3407
2024: #4042
2025: #4522
Ruff: astral-sh/ruff#20482
As always, the ideal is for all preview features to be stabilized, not including the unstable ones.
Ordered by most controversial to least:
multiline_string_handling (#1879)
Make expressions involving multiline strings more compact.
Input
textwrap.dedent(
"""\
This is a
multiline string
"""
)
MULTILINE = """
foobar
""".replace(
"\n", ""
)Output (off)
unchanged
Output (on)
textwrap.dedent("""\
This is a
multiline string
""")
MULTILINE = """
foobar
""".replace("\n", "")Fairly controversial. There were some issues in #4159 that were never fully fixed. Additionally, it's only been in preview since v25.11 (but the issues were resolved as-best-as-possible since v25.9).
However, we haven't gotten any further feedback since then. I think the current state is an improvement overall and we probably won't be able to do much else.
IMO, we should at least include it in the first v26 beta to get user feedback, even if we don't plan on stabilizing it.
wrap_long_dict_values_in_parens (#3440)
Add parentheses around long values in dictionaries. Deferred to next year
wrap_long_dict_values_in_parens (#3440)Input
my_dict = {
"a key in my dict": a_very_long_variable * and_a_very_long_function_call() / 100000.0
}Output (off)
unchanged
Output (on)
my_dict = {
"a key in my dict": (
a_very_long_variable * and_a_very_long_function_call() / 100000.0
)
}Could cause a lot of churn. Adds more parentheses and lines to reduce line length. Deferred in 2023 and 2024, then moved to unstable until 25.1, when the issues with it were resolved and it was moved to preview. Ruff has already decided this to be a non-priority (astral-sh/ruff#12856, #4123).
wrap_comprehension_in (#4699)
Wrap the wrap_comprehension_in (#4699)in clause of list and dictionary comprehensions across lines if it would otherwise exceed the maximum line length. Deferred to next year
Input
[a for graph_path_expression in refined_constraint.condition_as_predicate.variables]Output (off)
[
a
for graph_path_expression in refined_constraint.condition_as_predicate.variables
]Output (on)
[
a
for graph_path_expression in (
refined_constraint.condition_as_predicate.variables
)
]Could cause a lot of churn. Adds more parentheses and lines to reduce line length. Ruff has already decided this to be a non-priority (#4123).
remove_parens_from_assignment_lhs (#4865)
Remove unnecessary parentheses from the left-hand side of assignments while preserving magic trailing commas and intentional multiline formatting.
Input
(c, *_) = a()Output (off)
unchanged
Output (on)
c, *_ = a()An unobjective change, but can cause lots of churn, and was added very recently (Not released yet, will hopefully be in a 25.12).
fix_type_expansion_split (#4777)
Fix type expansions split in generic functions.
Input
def func1[T: (int, str)](a,): ...Output (off)
def func1[
T: (int, str)
](a,): ...Output (on)
def func1[T: (int, str)](
a,
): ...Causes a good amount of changes, but should be fairly objective.
standardize_type_comments (#4645)
Format type comments which have zero or more spaces between # and type: or between type: and value to # type: (value).
Input
# type: ignoreOutput (off)
unchanged
Output (on)
# type: ignoreCauses changes, but they are minimal, localized, and objective.
always_one_newline_after_import (#4489)
Always force one blank line after import statements, except when the line after the import is a comment or an import statement.
Input
from middleman.authentication import validate_oauth_token
logger = logging.getLogger(__name__)Output (off)
unchanged
Output (on)
from middleman.authentication import validate_oauth_token
logger = logging.getLogger(__name__)Only changes one part of each file, and changes are minimal and objective. Was deferred in 2025, just because it was new.
remove_parens_around_except_types (#4720)
Remove parentheses around multiple exception types in except and except* without as. See PEP 758 for details.
Input
try:
...
except (A, B, C):
...Output (off)
unchanged
Output (on)
try:
...
except A, B, C:
...New feature introduced in & gated to Python 3.14. No changes in most codebases, which are under 3.14. When there are changes, they're minimal, localized, and objective.
fix_module_docstring_detection (#4764)
Fix module docstrings being treated as normal strings if preceded by comments.
Input
# comment
"""
docstring
"""
from __future__ import annotationsOutput (off)
unchanged
Output (on)
# comment
"""
docstring
"""
from __future__ import annotationsBug fix; only changes one thing per file at max.
normalize_cr_newlines (#4710)
Add \r style newlines to the potential newlines to normalize file newlines both from and to.
Input
a[CR]
b[LF]
c[CR][LF]
#1[CR][LF]Output (off)
a[CR]
b[LF]
c[CR]
# 1[CR]Output (on)
a[CR]
b[CR]
c[CR]
# 1[CR]Bug fix, was only ever found by Fuzz and shouldn't cause any noticable source code changes in practice.
fix_fmt_skip_in_one_liners (#4800)
Fix # fmt: skip behavior on one-liner declarations, such as def foo(): return "mock" # fmt: skip, where previously the declaration would have been incorrectly collapsed.
Input
if True: print("this"); print("that") # fmt: skipOutput (off)
if True:
print("this"); print("that") # fmt: skipOutput (on)
unchanged
Purely a bug fix, shouldn't actually change any pre-formatted code.
Also, we should remember to regenerate _width_table.py for v26 (#4253).
IMO, everything fix_type_expansion_split and below should definitely be stabilized. I see arguments either way for the items above it. Discussion welcome!