Return list of raw (un-parsed) format specifiers in format string. Format specifiers don't include enclosing braces. We don't use regexp for this because they don't work well with nested/repeated patterns (both greedy and non-greedy), and these are heavily used internally for repres
(
format_value: str, ctx: Context, msg: MessageBuilder
)
| 230 | |
| 231 | |
| 232 | def find_non_escaped_targets( |
| 233 | format_value: str, ctx: Context, msg: MessageBuilder |
| 234 | ) -> list[tuple[str, int]] | None: |
| 235 | """Return list of raw (un-parsed) format specifiers in format string. |
| 236 | |
| 237 | Format specifiers don't include enclosing braces. We don't use regexp for |
| 238 | this because they don't work well with nested/repeated patterns |
| 239 | (both greedy and non-greedy), and these are heavily used internally for |
| 240 | representation of f-strings. |
| 241 | |
| 242 | Return None in case of an error. |
| 243 | """ |
| 244 | result = [] |
| 245 | next_spec = "" |
| 246 | pos = 0 |
| 247 | nesting = 0 |
| 248 | while pos < len(format_value): |
| 249 | c = format_value[pos] |
| 250 | if not nesting: |
| 251 | # Skip any paired '{{' and '}}', enter nesting on '{', report error on '}'. |
| 252 | if c == "{": |
| 253 | if pos < len(format_value) - 1 and format_value[pos + 1] == "{": |
| 254 | pos += 1 |
| 255 | else: |
| 256 | nesting = 1 |
| 257 | if c == "}": |
| 258 | if pos < len(format_value) - 1 and format_value[pos + 1] == "}": |
| 259 | pos += 1 |
| 260 | else: |
| 261 | msg.fail( |
| 262 | "Invalid conversion specifier in format string: unexpected }", |
| 263 | ctx, |
| 264 | code=codes.STRING_FORMATTING, |
| 265 | ) |
| 266 | return None |
| 267 | else: |
| 268 | # Adjust nesting level, then either continue adding chars or move on. |
| 269 | if c == "{": |
| 270 | nesting += 1 |
| 271 | if c == "}": |
| 272 | nesting -= 1 |
| 273 | if nesting: |
| 274 | next_spec += c |
| 275 | else: |
| 276 | result.append((next_spec, pos - len(next_spec))) |
| 277 | next_spec = "" |
| 278 | pos += 1 |
| 279 | if nesting: |
| 280 | msg.fail( |
| 281 | "Invalid conversion specifier in format string: unmatched {", |
| 282 | ctx, |
| 283 | code=codes.STRING_FORMATTING, |
| 284 | ) |
| 285 | return None |
| 286 | return result |
| 287 | |
| 288 | |
| 289 | class StringFormatterChecker: |
no test coverage detected
searching dependent graphs…