From 40e3ecb119418b167404e024e1a2344306a86f31 Mon Sep 17 00:00:00 2001 From: Vincent Gao Date: Sat, 20 Jun 2026 21:40:32 +0200 Subject: [PATCH] fix: render dict `**`-unpacking as `**value` instead of `None: value` `ExprDict.iterate` rendered the `None` key that the AST uses to mark a `**`-unpacked value as a literal `None: value`, producing invalid Python (e.g. `{None: base, 'x': 1}` for `{**base, 'x': 1}`). Render those entries as `**value`. A genuine `None` literal key is an `ast.Constant` rather than the AST's unpacking marker, so it is preserved. --- .../src/griffe/_internal/expressions.py | 2 +- packages/griffelib/tests/test_expressions.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/griffelib/src/griffe/_internal/expressions.py b/packages/griffelib/src/griffe/_internal/expressions.py index 4043c04b..df959bee 100644 --- a/packages/griffelib/src/griffe/_internal/expressions.py +++ b/packages/griffelib/src/griffe/_internal/expressions.py @@ -452,7 +452,7 @@ class ExprDict(Expr): def iterate(self, *, flat: bool = True) -> Iterator[str | Expr]: yield "{" yield from _join( - (("None" if key is None else key, ": ", value) for key, value in zip(self.keys, self.values, strict=False)), + (("**", value) if key is None else (key, ": ", value) for key, value in zip(self.keys, self.values, strict=False)), ", ", flat=flat, ) diff --git a/packages/griffelib/tests/test_expressions.py b/packages/griffelib/tests/test_expressions.py index e0b2267a..3fcd831b 100644 --- a/packages/griffelib/tests/test_expressions.py +++ b/packages/griffelib/tests/test_expressions.py @@ -241,3 +241,22 @@ def test_render_dict_comprehension() -> None: """, ) as module: assert str(module["d"].value) == "{k: v for k, v in items if k}" + + +def test_render_dict_with_unpacking() -> None: + """Assert dictionaries using `**`-unpacking are rendered correctly. + + The AST marks an unpacked value with a `None` key, which must render as + `**value` rather than as a literal `None` key. A genuine `None` literal key + (an `ast.Constant`, not the AST's unpacking marker) must be preserved. + """ + with temporary_visited_module( + """ + a = {**base, "x": 1} + b = {**d1, **d2} + c = {None: 1, "y": 2} + """, + ) as module: + assert str(module["a"].value) == "{**base, 'x': 1}" + assert str(module["b"].value) == "{**d1, **d2}" + assert str(module["c"].value) == "{None: 1, 'y': 2}"