mirror of https://github.com/astral-sh/ruff
## Summary
This PR fixes the bug where the formatter would format an f-string and
could potentially change the AST.
For a triple-quoted f-string, the element can't be formatted into
multiline if it has a format specifier because otherwise the newline
would be treated as part of the format specifier.
Given the following f-string:
```python
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
variable:.3f} ddddddddddddddd eeeeeeee"""
```
The formatter sees that the f-string is already multiline so it assumes
that it can contain line breaks i.e., broken into multiple lines. But,
in this specific case we can't format it as:
```python
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
variable:.3f
} ddddddddddddddd eeeeeeee"""
```
Because the format specifier string would become ".3f\n", which is not
the original string (`.3f`).
If the original source code already contained a newline, they'll be
preserved. For example:
```python
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {
variable:.3f
} ddddddddddddddd eeeeeeee"""
```
The above will be formatted as:
```py
f"""aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbbbbb ccccccccccc {variable:.3f
} ddddddddddddddd eeeeeeee"""
```
Note that the newline after `.3f` is part of the format specifier which
needs to be preserved.
The Python version is irrelevant in this case.
fixes: #10040
## Test Plan
Add some test cases to verify this behavior.
|
||
|---|---|---|
| .. | ||
| carriage_return | ||
| expression | ||
| fmt_on_off | ||
| fmt_skip | ||
| parentheses | ||
| range_formatting | ||
| statement | ||
| stub_files | ||
| .editorconfig | ||
| blank_line_before_class_docstring.options.json | ||
| blank_line_before_class_docstring.py | ||
| docstring.options.json | ||
| docstring.py | ||
| docstring_code_examples.options.json | ||
| docstring_code_examples.py | ||
| docstring_code_examples_crlf.options.json | ||
| docstring_code_examples_crlf.py | ||
| docstring_code_examples_dynamic_line_width.options.json | ||
| docstring_code_examples_dynamic_line_width.py | ||
| docstring_newlines.py | ||
| docstring_tab_indentation.options.json | ||
| docstring_tab_indentation.py | ||
| empty_multiple_trailing_newlines.py | ||
| empty_now_newline.py | ||
| empty_trailing_newline.py | ||
| empty_whitespace.py | ||
| form_feed.py | ||
| module_dangling_comment1.py | ||
| module_dangling_comment2.py | ||
| multiline_string_deviations.py | ||
| newlines.py | ||
| newlines.pyi | ||
| notebook_docstring.options.json | ||
| notebook_docstring.py | ||
| preview.options.json | ||
| preview.py | ||
| quote_style.options.json | ||
| quote_style.py | ||
| skip_magic_trailing_comma.options.json | ||
| skip_magic_trailing_comma.py | ||
| tab_width.options.json | ||
| tab_width.py | ||
| trailing_comments.py | ||
| trivia.py | ||