fix(python): count control flow inside f-string interpolations (#317)#481
Merged
terryyin merged 1 commit intoJun 11, 2026
Merged
Conversation
…yin#317) An f-string was emitted as a single opaque token, so control-flow keywords inside its {...} interpolations (a comprehension 'for', a ternary 'if', 'and'/'or') never reached the condition counter and cyclomatic complexity was undercounted. f"{[x for x in items]}" scored 1 instead of 2. The tokenizer emits the f-string prefix ('f', 'rf', ...) as its own token right before the string body, so generate_tokens now watches for a prefix followed by a string and re-tokenizes each {...} interpolation as Python code (recursing for nested f-strings). Literal text and the '{{'/'}}' escapes stay literal, and keywords inside nested string literals are not counted because they remain string tokens.
Owner
|
thanks for the PR. I also added a small follow up a46756d |
Contributor
Author
|
nice, that brace-in-string skip is a good catch - my depth counter would've tripped on a and you're right the original #317 (backslash at the end of a |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #317.
lizard doesn't count control flow hidden inside f-string interpolations. this function:
reports CCN 1. drop the f-string wrapper and it's correctly 2 - the comprehension
foris a branch either way.why
the tokenizer treats a whole f-string as one opaque string token, so the
for/if/and/orinside{...}never reach the condition counter. same blind spot for a ternaryifor a logical operator inside an interpolation.the fix
generate_tokensnow re-tokenizes the interpolations. the tokenizer already emits the f-string prefix (f,rf, ...) as its own token right before the string body, so it watches for a prefix followed by a string, splits the body into literal chunks (kept as string tokens) and interpolation expressions, and tokenizes each expression as the python code it is - so the inner keywords get counted. nested f-strings recurse. the splitting follows what the TypeScript reader already does for template-literal${...}; this additionally tokenizes the expression so the conditions count.what it gets right, with tests:
{{and}}stay literal braces, not interpolationsf"{x or 'skip if empty'}") doesn't count - only the realordoestests
8 cases in
testPython.pycovering comprehensions, ternaries, logical operators, nested f-strings, the escape and nested-string guards, and parity with the non-f-string equivalent. full suite green - the one unrelatedtest_gitignore_filterfailure is pre-existing onmaster.