|
30 | 30 | # an expression inside a formatted value
|
31 | 31 | (
|
32 | 32 | f'{1}'
|
33 |
| - # comment |
| 33 | + # comment 1 |
34 | 34 | ''
|
35 | 35 | )
|
36 | 36 |
|
37 | 37 | (
|
38 |
| - f'{1}' # comment |
| 38 | + f'{1}' # comment 2 |
39 | 39 | f'{2}'
|
40 | 40 | )
|
41 | 41 |
|
42 | 42 | (
|
43 | 43 | f'{1}'
|
44 |
| - f'{2}' # comment |
| 44 | + f'{2}' # comment 3 |
45 | 45 | )
|
46 | 46 |
|
47 | 47 | (
|
48 |
| - 1, ( # comment |
| 48 | + 1, ( # comment 4 |
49 | 49 | f'{2}'
|
50 | 50 | )
|
51 | 51 | )
|
52 | 52 |
|
53 | 53 | (
|
54 | 54 | (
|
55 | 55 | f'{1}'
|
56 |
| - # comment |
| 56 | + # comment 5 |
57 | 57 | ),
|
58 | 58 | 2
|
59 | 59 | )
|
|
62 | 62 | x = f'''a{""}b'''
|
63 | 63 | y = f'''c{1}d"""e'''
|
64 | 64 | z = f'''a{""}b''' f'''c{1}d"""e'''
|
| 65 | + |
| 66 | +# F-String formatting test cases (Preview) |
| 67 | + |
| 68 | +# Simple expression with a mix of debug expression and comments. |
| 69 | +x = f"{a}" |
| 70 | +x = f"{ |
| 71 | + a = }" |
| 72 | +x = f"{ # comment 6 |
| 73 | + a }" |
| 74 | +x = f"{ # comment 7 |
| 75 | + a = }" |
| 76 | + |
| 77 | +# Remove the parentheses as adding them doesn't make then fit within the line length limit. |
| 78 | +# This is similar to how we format it before f-string formatting. |
| 79 | +aaaaaaaaaaa = ( |
| 80 | + f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc + dddddddd } cccccccccc" |
| 81 | +) |
| 82 | +# Here, we would use the best fit layout to put the f-string indented on the next line |
| 83 | +# similar to the next example. |
| 84 | +aaaaaaaaaaa = f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" |
| 85 | +aaaaaaaaaaa = ( |
| 86 | + f"asaaaaaaaaaaaaaaaa { aaaaaaaaaaaa + bbbbbbbbbbbb + ccccccccccccccc } cccccccccc" |
| 87 | +) |
| 88 | + |
| 89 | +# This should never add the optional parentheses because even after adding them, the |
| 90 | +# f-string exceeds the line length limit. |
| 91 | +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" |
| 92 | +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" |
| 93 | +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 8 |
| 94 | + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" } ccccccccccccccc" |
| 95 | +x = f"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa { # comment 9 |
| 96 | + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbb" = } ccccccccccccccc" |
| 97 | + |
| 98 | +# Multiple larger expressions which exceeds the line length limit. Here, we need to decide |
| 99 | +# whether to split at the first or second expression. This should work similarly to the |
| 100 | +# assignment statement formatting where we split from right to left in preview mode. |
| 101 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" |
| 102 | + |
| 103 | +# The above example won't split but when we start introducing line breaks: |
| 104 | +x = f"aaaaaaaaaaaa { |
| 105 | + bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" |
| 106 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb |
| 107 | + } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" |
| 108 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { |
| 109 | + ddddddddddddddd } eeeeeeeeeeeeee" |
| 110 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb } cccccccccccccccccccc { ddddddddddddddd |
| 111 | + } eeeeeeeeeeeeee" |
| 112 | + |
| 113 | +# But, in case comments are present, we would split at the expression containing the |
| 114 | +# comments: |
| 115 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb # comment 10 |
| 116 | + } cccccccccccccccccccc { ddddddddddddddd } eeeeeeeeeeeeee" |
| 117 | +x = f"aaaaaaaaaaaa { bbbbbbbbbbbbbb |
| 118 | + } cccccccccccccccccccc { # comment 11 |
| 119 | + ddddddddddddddd } eeeeeeeeeeeeee" |
| 120 | + |
| 121 | +# Here, the expression part itself starts with a curly brace so we need to add an extra |
| 122 | +# space between the opening curly brace and the expression. |
| 123 | +x = f"{ {'x': 1, 'y': 2} }" |
| 124 | +# Although the extra space isn't required before the ending curly brace, we add it for |
| 125 | +# consistency. |
| 126 | +x = f"{ {'x': 1, 'y': 2}}" |
| 127 | +x = f"{ {'x': 1, 'y': 2} = }" |
| 128 | +x = f"{ # comment 12 |
| 129 | + {'x': 1, 'y': 2} }" |
| 130 | +x = f"{ # comment 13 |
| 131 | + {'x': 1, 'y': 2} = }" |
| 132 | + |
| 133 | +# But, in this case, we would split the expression itself because it exceeds the line |
| 134 | +# length limit so we need not add the extra space. |
| 135 | +xxxxxxx = f"{ |
| 136 | + {'aaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbb', 'ccccccccccccccccccccc'} |
| 137 | +}" |
| 138 | +# And, split the expression itself because it exceeds the line length. |
| 139 | +xxxxxxx = f"{ |
| 140 | + {'aaaaaaaaaaaaaaaaaaaaaaaaa', 'bbbbbbbbbbbbbbbbbbbbbbbbbbb', 'cccccccccccccccccccccccccc'} |
| 141 | +}" |
| 142 | + |
| 143 | +# Quotes |
| 144 | +f"foo 'bar' {x}" |
| 145 | +f"foo \"bar\" {x}" |
| 146 | +f'foo "bar" {x}' |
| 147 | +f'foo \'bar\' {x}' |
| 148 | +f"foo {"bar"}" |
| 149 | +f"foo {'\'bar\''}" |
| 150 | + |
| 151 | +# Here, the formatter will remove the escapes which is correct because they aren't allowed |
| 152 | +# pre 3.12. This means we can assume that the f-string is used in the context of 3.12. |
| 153 | +f"foo {'\"bar\"'}" |
| 154 | + |
| 155 | + |
| 156 | +# Triple-quoted strings |
| 157 | +# It's ok to use the same quote char for the inner string if it's single-quoted. |
| 158 | +f"""test {'inner'}""" |
| 159 | +f"""test {"inner"}""" |
| 160 | +# But if the inner string is also triple-quoted then we should preserve the existing quotes. |
| 161 | +f"""test {'''inner'''}""" |
| 162 | + |
| 163 | +# Magic trailing comma |
| 164 | +# |
| 165 | +# The expression formatting will result in breaking it across multiple lines with a |
| 166 | +# trailing comma but as the expression isn't already broken, we will remove all the line |
| 167 | +# breaks which results in the trailing comma being present. This test case makes sure |
| 168 | +# that the trailing comma is removed as well. |
| 169 | +f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee']} aaaaaaa" |
| 170 | + |
| 171 | +# And, if the trailing comma is already present, we still need to remove it. |
| 172 | +f"aaaaaaa {['aaaaaaaaaaaaaaa', 'bbbbbbbbbbbbb', 'ccccccccccccccccc', 'ddddddddddddddd', 'eeeeeeeeeeeeee',]} aaaaaaa" |
| 173 | + |
| 174 | +# Keep this Multiline by breaking it at the square brackets. |
| 175 | +f"""aaaaaa {[ |
| 176 | + xxxxxxxx, |
| 177 | + yyyyyyyy, |
| 178 | +]} ccc""" |
| 179 | + |
| 180 | +# Add the magic trailing comma because the elements don't fit within the line length limit |
| 181 | +# when collapsed. |
| 182 | +f"aaaaaa {[ |
| 183 | + xxxxxxxxxxxx, |
| 184 | + xxxxxxxxxxxx, |
| 185 | + xxxxxxxxxxxx, |
| 186 | + xxxxxxxxxxxx, |
| 187 | + xxxxxxxxxxxx, |
| 188 | + xxxxxxxxxxxx, |
| 189 | + yyyyyyyyyyyy |
| 190 | +]} ccccccc" |
| 191 | + |
| 192 | +# Remove the parenthese because they aren't required |
| 193 | +xxxxxxxxxxxxxxx = ( |
| 194 | + f"aaaaaaaaaaaaaaaa bbbbbbbbbbbbbbb { |
| 195 | + xxxxxxxxxxx # comment 14 |
| 196 | + + yyyyyyyyyy |
| 197 | + } dddddddddd" |
| 198 | +) |
| 199 | + |
| 200 | +# Comments |
| 201 | + |
| 202 | +# No comments should be dropped! |
| 203 | +f"{ # comment 15 |
| 204 | + # comment 16 |
| 205 | + foo # comment 17 |
| 206 | + # comment 18 |
| 207 | +}" # comment 19 |
| 208 | +# comment 20 |
| 209 | + |
| 210 | +# Conversion flags |
| 211 | +# |
| 212 | +# This is not a valid Python code because of the additional whitespace between the `!` |
| 213 | +# and conversion type. But, our parser isn't strict about this. This should probably be |
| 214 | +# removed once we have a strict parser. |
| 215 | +x = f"aaaaaaaaa { x ! r }" |
| 216 | + |
| 217 | +# Even in the case of debug expresions, we only need to preserve the whitespace within |
| 218 | +# the expression part of the replacement field. |
| 219 | +x = f"aaaaaaaaa { x = ! r }" |
| 220 | + |
| 221 | +# Combine conversion flags with format specifiers |
| 222 | +x = f"{x = ! s |
| 223 | + :>0 |
| 224 | +
|
| 225 | + }" |
| 226 | +# This is interesting. There can be a comment after the format specifier but only if it's |
| 227 | +# on it's own line. Refer to https://github.com/astral-sh/ruff/pull/7787 for more details. |
| 228 | +# We'll format is as trailing comments. |
| 229 | +x = f"{x !s |
| 230 | + :>0 |
| 231 | + # comment 21 |
| 232 | + }" |
| 233 | + |
| 234 | +x = f""" |
| 235 | +{ # comment 22 |
| 236 | + x = :.0{y # comment 23 |
| 237 | + }f}""" |
| 238 | + |
| 239 | +# Here, the debug expression is in a nested f-string so we should start preserving |
| 240 | +# whitespaces from that point onwards. This means we should format the outer f-string. |
| 241 | +x = f"""{"foo " + # comment 24 |
| 242 | + f"{ x = |
| 243 | +
|
| 244 | + }" # comment 25 |
| 245 | + } |
| 246 | + """ |
| 247 | + |
| 248 | +# Mix of various features. |
| 249 | +f"{ # comment 26 |
| 250 | + foo # after foo |
| 251 | + :>{ |
| 252 | + x # after x |
| 253 | + } |
| 254 | + # comment 27 |
| 255 | + # comment 28 |
| 256 | +} woah {x}" |
| 257 | + |
| 258 | +# Indentation |
| 259 | + |
| 260 | +# What should be the indentation? |
| 261 | +# https://github.com/astral-sh/ruff/discussions/9785#discussioncomment-8470590 |
| 262 | +if indent0: |
| 263 | + if indent1: |
| 264 | + if indent2: |
| 265 | + foo = f"""hello world |
| 266 | +hello { |
| 267 | + f"aaaaaaa { |
| 268 | + [ |
| 269 | + 'aaaaaaaaaaaaaaaaaaaaa', |
| 270 | + 'bbbbbbbbbbbbbbbbbbbbb', |
| 271 | + 'ccccccccccccccccccccc', |
| 272 | + 'ddddddddddddddddddddd' |
| 273 | + ] |
| 274 | + } bbbbbbbb" + |
| 275 | + [ |
| 276 | + 'aaaaaaaaaaaaaaaaaaaaa', |
| 277 | + 'bbbbbbbbbbbbbbbbbbbbb', |
| 278 | + 'ccccccccccccccccccccc', |
| 279 | + 'ddddddddddddddddddddd' |
| 280 | + ] |
| 281 | + } -------- |
| 282 | +""" |
0 commit comments