Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,7 @@
# mapping unique short aliases to a base URL and a prefix.
# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
extlinks = {
"oss-fuzz": ("https://issues.oss-fuzz.com/issues/%s", "#%s"),
"pypi": ("https://pypi.org/project/%s/", "%s"),
"source": (SOURCE_URI, "%s"),
}
Expand Down
10 changes: 7 additions & 3 deletions Lib/_ast_unparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -738,9 +738,13 @@ def visit_SetComp(self, node):

def visit_DictComp(self, node):
with self.delimit("{", "}"):
self.traverse(node.key)
self.write(": ")
self.traverse(node.value)
if node.value:
self.traverse(node.key)
self.write(": ")
self.traverse(node.value)
else:
self.write("**")
self.traverse(node.key)
for gen in node.generators:
self.traverse(gen)

Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_future_stmt/test_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,7 @@ def test_annotations(self):
eq("(i ** 2 + j for i in (1, 2, 3) for j in (1, 2, 3))")
eq("{i: 0 for i in (1, 2, 3)}")
eq("{i: j for i, j in ((1, 'a'), (2, 'b'), (3, 'c'))}")
eq("{**x for x in ()}")
eq("[(x, y) for x, y in (a, b)]")
eq("[(x,) for x, in (a,)]")
eq("Python3 > Python2 > COBOL")
Comment thread
StanFromIreland marked this conversation as resolved.
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/test_unparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,10 @@ def test_set_comprehension(self):
def test_dict_comprehension(self):
self.check_ast_roundtrip("{x: x*x for x in range(10)}")

def test_dict_comprehension_unpacking(self):
self.check_ast_roundtrip("{**x for x in ()}")
self.check_ast_roundtrip("{**x for x in range(10)}")
Comment thread
StanFromIreland marked this conversation as resolved.

def test_class_decorators(self):
self.check_ast_roundtrip(class_decorator)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash in AST unparser when unparsing dict comprehension unpacking.
Found by OSS Fuzz in :oss-fuzz:`489790200`.
12 changes: 9 additions & 3 deletions Python/ast_unparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,9 +464,15 @@ static int
append_ast_dictcomp(PyUnicodeWriter *writer, expr_ty e)
{
APPEND_CHAR('{');
APPEND_EXPR(e->v.DictComp.key, PR_TEST);
APPEND_STR(": ");
APPEND_EXPR(e->v.DictComp.value, PR_TEST);
if (e->v.DictComp.value) {
APPEND_EXPR(e->v.DictComp.key, PR_TEST);
APPEND_STR(": ");
APPEND_EXPR(e->v.DictComp.value, PR_TEST);
}
else {
APPEND_STR("**");
APPEND_EXPR(e->v.DictComp.key, PR_TEST);
}
APPEND(comprehensions, e->v.DictComp.generators);
APPEND_CHAR_FINISH('}');
}
Expand Down