Yields spans corresponding to expressions in a given f-string. Spans are half-open ranges (left inclusive, right exclusive). Assumes the input string is a valid f-string, but will not crash if the input string is invalid.
(s: str)
| 1329 | |
| 1330 | |
| 1331 | def iter_fexpr_spans(s: str) -> Iterator[tuple[int, int]]: |
| 1332 | """ |
| 1333 | Yields spans corresponding to expressions in a given f-string. |
| 1334 | Spans are half-open ranges (left inclusive, right exclusive). |
| 1335 | Assumes the input string is a valid f-string, but will not crash if the input |
| 1336 | string is invalid. |
| 1337 | """ |
| 1338 | stack: list[int] = [] # our curly paren stack |
| 1339 | i = 0 |
| 1340 | while i < len(s): |
| 1341 | if s[i] == "{": |
| 1342 | # if we're in a string part of the f-string, ignore escaped curly braces |
| 1343 | if not stack and i + 1 < len(s) and s[i + 1] == "{": |
| 1344 | i += 2 |
| 1345 | continue |
| 1346 | stack.append(i) |
| 1347 | i += 1 |
| 1348 | continue |
| 1349 | |
| 1350 | if s[i] == "}": |
| 1351 | if not stack: |
| 1352 | i += 1 |
| 1353 | continue |
| 1354 | j = stack.pop() |
| 1355 | # we've made it back out of the expression! yield the span |
| 1356 | if not stack: |
| 1357 | yield (j, i + 1) |
| 1358 | i += 1 |
| 1359 | continue |
| 1360 | |
| 1361 | # if we're in an expression part of the f-string, fast-forward through strings |
| 1362 | # note that backslashes are not legal in the expression portion of f-strings |
| 1363 | if stack: |
| 1364 | delim = None |
| 1365 | if s[i : i + 3] in ("'''", '"""'): |
| 1366 | delim = s[i : i + 3] |
| 1367 | elif s[i] in ("'", '"'): |
| 1368 | delim = s[i] |
| 1369 | if delim: |
| 1370 | i += len(delim) |
| 1371 | while i < len(s) and s[i : i + len(delim)] != delim: |
| 1372 | i += 1 |
| 1373 | i += len(delim) |
| 1374 | continue |
| 1375 | i += 1 |
| 1376 | |
| 1377 | |
| 1378 | def fstring_contains_expr(s: str) -> bool: |