Skip to content

fix: Make stringified expressions valid and faithful Python#478

Open
gaoflow wants to merge 2 commits into
mkdocstrings:mainfrom
gaoflow:fix-expr-stringification-fidelity
Open

fix: Make stringified expressions valid and faithful Python#478
gaoflow wants to merge 2 commits into
mkdocstrings:mainfrom
gaoflow:fix-expr-stringification-fidelity

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

For reviewers

  • I did not use AI
  • I used AI and thoroughly reviewed every code/docs change

Description of the change

str(Expr) could emit invalid or semantically different Python for lambda parameter markers, formatted strings, generator expressions, integer-literal attributes, slices, and precedence-sensitive contexts.

This fixes the shared rendering and building paths and adds a parse → Expr → str → parse matrix that checks AST equivalence across expression shapes and contexts. The full griffelib suite passes on Python 3.12; the focused expression and node suites pass on Python 3.10 and 3.14.

Relevant resources

Fixes several classes of invalid or unfaithful output from str(expr),
found with a systematic round-trip audit of expression rendering
(parse -> Expr -> str -> re-parse must succeed and be AST-equivalent):

- Lambda parameter sections: the keyword-only marker was emitted even
  after *args (lambda *a, *, z=2: z), section markers were emitted after
  * and ** (lambda x, */, a: a), and the / marker was dropped when all
  parameters are positional-only (lambda x, /: x rendered lambda x: x).
- F-string conversions and format specifiers were dropped entirely:
  f'{x!r:>10}' rendered as f'{x}'. Same for t-string interpolations on
  Python 3.14, which also lost the quotes of string constants.
- Missing required parentheses: generator expressions in grouping
  contexts ({1: g for g in y} re-parses as a dict comprehension), walrus
  assignments in dict keys/values, slice bounds and comprehension
  conditions, integer-literal attribute access (1.bit_length()), and
  lambdas/walrus in f-string replacement fields where the colon would
  start the format specifier.
- The subscript_slice context flag leaked into slice bounds, marking
  tuples implicit: o[(a, b):y] rendered as o[a, b:y].
- Spurious parentheses from iterate() call sites relying on the ATOMIC
  default: f(a=(b + c)), o[(a + b):], [(x + 1) for x in y], etc.
@pawamoy

pawamoy commented Jul 16, 2026

Copy link
Copy Markdown
Member

Thanks for your contribution. Again, please follow our PR template.

Comment thread packages/griffelib/src/griffe/_internal/expressions.py
Comment thread packages/griffelib/src/griffe/_internal/expressions.py
Comment thread packages/griffelib/src/griffe/_internal/expressions.py Outdated
Comment thread packages/griffelib/src/griffe/_internal/expressions.py Outdated
Comment thread packages/griffelib/tests/test_expressions.py Outdated
@gaoflow

gaoflow commented Jul 16, 2026

Copy link
Copy Markdown
Contributor Author

Updated the PR body to follow the template, including the AI-use checkbox. I also pushed f79da51 for the review suggestions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants