fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix: keep parentheses around top-level named expressions (walrus) - #480

Closed
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses
Closed

fix: keep parentheses around top-level named expressions (walrus)#480
chuenchen309 wants to merge 1 commit into
mkdocstrings:mainfrom
chuenchen309:fix/top-level-walrus-parentheses

Conversation

@chuenchen309

Copy link
Copy Markdown

The bug

A top-level named expression (walrus, :=) — extracted as a whole attribute value or parameter default — is rendered without its surrounding parentheses, producing invalid Python:

importgriffe# module: x = (n := 1); def f(a=(b := compute())): ...m=griffe.load("mod")
str(m["x"].value) # 'n := 1' -> `x = n := 1` is a SyntaxErrorstr(m["f"].parameters["a"].default) # 'b := compute()' -> invalid as a default

ast.unparse (the authoritative "valid, faithful Python" renderer) always parenthesizes a NamedExpr here → '(n := 1)'. Griffe drops the parens only at the top level.

Root cause

Expr.__str__ (packages/griffelib/src/griffe/_internal/expressions.py) renders the top expression by iterating flatly, which bypasses the _yield/precedence logic that adds walrus parentheses in nested positions. ExprNamedExpr deliberately does not self-parenthesize (that would double-wrap when nested), and a top-level expression has no enclosing context to add them — so a standalone walrus never gets parenthesized.

This is exactly the gap left by commit eb85f0d ("Make stringified expressions valid and faithful Python"): its property test test_expressions_stay_valid_and_equivalent embeds (w := 1) in ~26 contexts, but every one wraps the expression — none renders it standalone.

The fix

Wrap a top-level ExprNamedExpr in parentheses in __str__, matching ast.unparse. Nested walruses are still parenthesized by their enclosing context (not by __str__), so there is no double-wrapping.

Verification

  • The repro now renders (n := 1) and (b := compute()); nested cases are unchanged ({'a': (w := 1)}, foo(k := 2) — single parens / valid).
  • Added "%s" (the standalone context) to the _expression_contexts property test — on main it fails on exactly [%s-(w := 1)] and nothing else — plus an explicit test_top_level_named_expression_keeps_parentheses.
  • tests/test_expressions.py → 910 passed / 16 skipped; tests/test_nodes.py → 141 passed. No regression.

Reachability: X = (n := compute()) (module/class attribute via walrus) and the shared-mutable-default idiom def f(cache=(_cache := {})) are ordinary user code; mkdocstrings renders these extracted strings straight into signatures/attribute docs, so the invalid output is user-visible.

Not a duplicate — the only related history, closed#152, is a different build-time KeyError for a walrus in a comprehension; no open issue/PR concerns top-level walrus rendering.


This PR was authored by an AI coding agent (Claude Code) running on this account: the AI found the bug, ran the repro, wrote the test, and wrote this description. The human account holder reviews every change and is accountable for it. The verification above is real and re-runnable from the diff. If this isn't the kind of contribution you want, say so and I'll close it.

Expr.__str__ rendered the top-level expression by iterating flatly,
bypassing the precedence machinery that parenthesizes a named expression
(walrus, :=) in nested positions. A named expression extracted as a whole
value — an attribute value or a parameter default — was therefore rendered
without its parentheses, producing invalid Python: `x = n := 1` is a
SyntaxError, and `def f(a=b := 1)` likewise.
Wrap a top-level ExprNamedExpr in parentheses, matching ast.unparse.
Nested occurrences are still parenthesized by their enclosing context, so
there is no double-wrapping.

@pawamoypawamoy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution @chuenchen309. While I appreciate the disclaimer at the end, you could have simply followed our PR template. Please do that.

Comment on lines +168 to +173
# A top-level named expression (walrus) is invalid Python without surrounding
# parentheses, e.g. as an attribute value or a parameter default. Nested occurrences
# are parenthesized by their enclosing context, but the top level has none.
if isinstance(self, ExprNamedExpr):
return f"({rendered})"
return rendered

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of including code for a specific subclass in the main __str__ method. Can't we play with the precedence system? Or can't we at least override __str__ in ExprNamedExpr (calling super() then wrapping in parens)?

@lprnmnslprnmns mentioned this pull request Sep 1, 2026
2 tasks
@pawamoy

Copy link
Copy Markdown
Member

No answer from contributor, closing.

Sign up for freeto 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.

Unhandled case of a walrus operator in the output part of a list comprehension

2 participants

@chuenchen309@pawamoy