|
80 | 80 | normalize_string_prefix, |
81 | 81 | normalize_string_quotes, |
82 | 82 | normalize_unicode_escape_sequences, |
| 83 | + str_width, |
83 | 84 | ) |
84 | 85 | from black.trans import ( |
85 | 86 | CannotTransform, |
@@ -770,7 +771,9 @@ def _rhs( |
770 | 771 | # *current* transformation fits in the line length. This is true only |
771 | 772 | # for simple cases. All others require running more transforms via |
772 | 773 | # `transform_line()`. This check doesn't know if those would succeed. |
773 | | - if is_line_short_enough(lines[0], mode=mode): |
| 774 | + if is_line_short_enough(lines[0], mode=mode) or ( |
| 775 | + omit and _over_length_only_due_to_subscript_comment(lines[0], mode) |
| 776 | + ): |
774 | 777 | yield from lines |
775 | 778 | return |
776 | 779 |
|
@@ -2013,6 +2016,37 @@ def generate_trailers_to_omit(line: Line, line_length: int) -> Iterator[set[Leaf |
2013 | 2016 | closing_bracket = leaf |
2014 | 2017 |
|
2015 | 2018 |
|
| 2019 | +def _over_length_only_due_to_subscript_comment(line: Line, mode: Mode) -> bool: |
| 2020 | + """Return True if `line` only exceeds `mode.line_length` because of an inline |
| 2021 | + comment attached to a subscript opening bracket (`[`). |
| 2022 | +
|
| 2023 | + This is the shape produced by the original of the issue #4733 reproducer: |
| 2024 | + a comment inside the annotation's subscript brackets renders at the end of |
| 2025 | + the head line after Black splits the statement, pushing it past the limit. |
| 2026 | + Taking the FORCE_OPTIONAL_PARENTHESES "second opinion" in that case wraps |
| 2027 | + the annotation in extra parens and migrates the comment outside the |
| 2028 | + subscript, which then oscillates on the next formatter pass. |
| 2029 | + """ |
| 2030 | + if not line.leaves: |
| 2031 | + return False |
| 2032 | + # The over-length must be caused entirely by a trailing comment. |
| 2033 | + indent = " " * line.depth |
| 2034 | + leaves_iter = iter(line.leaves) |
| 2035 | + first = next(leaves_iter) |
| 2036 | + text_without_comments = f"{first.prefix}{indent}{first.value}" |
| 2037 | + text_without_comments += "".join(str(leaf) for leaf in leaves_iter) |
| 2038 | + if str_width(text_without_comments) > mode.line_length: |
| 2039 | + return False |
| 2040 | + # And the comment must be attached to a subscript opening bracket. |
| 2041 | + for leaf_id, comments in line.comments.items(): |
| 2042 | + if not comments: |
| 2043 | + continue |
| 2044 | + leaf = next((lf for lf in line.leaves if id(lf) == leaf_id), None) |
| 2045 | + if leaf is None or leaf.type != token.LSQB: |
| 2046 | + return False |
| 2047 | + return True |
| 2048 | + |
| 2049 | + |
2016 | 2050 | def run_transformer( |
2017 | 2051 | line: Line, |
2018 | 2052 | transform: Transformer, |
@@ -2040,6 +2074,12 @@ def run_transformer( |
2040 | 2074 | or result[0].contains_uncollapsable_type_comments() |
2041 | 2075 | or result[0].contains_unsplittable_type_ignore() |
2042 | 2076 | or is_line_short_enough(result[0], mode=mode) |
| 2077 | + # result[0] only exceeds the length because of a comment attached to a |
| 2078 | + # subscript opening bracket. Taking the FORCE_OPTIONAL_PARENTHESES |
| 2079 | + # "second opinion" wraps the annotation in extra invisible parens and |
| 2080 | + # migrates the comment outside the subscript, which then oscillates with |
| 2081 | + # a deeper-bracket split on the next formatter pass (issue #4733). |
| 2082 | + or _over_length_only_due_to_subscript_comment(result[0], mode) |
2043 | 2083 | # If any leaves have no parents (which _can_ occur since |
2044 | 2084 | # `transform(line)` potentially destroys the line's underlying node |
2045 | 2085 | # structure), then we can't proceed. Doing so would cause the below |
|
0 commit comments