# 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"),
}
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)
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 for x in ()]")
eq("[(x, y) for x, y in (a, b)]")
eq("[(x,) for x, in (a,)]")
eq("Python3 > Python2 > COBOL")
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)}")
+ self.check_ast_roundtrip("[*x for x in ()]")
+
def test_class_decorators(self):
self.check_ast_roundtrip(class_decorator)
--- /dev/null
+Fix crash in AST unparser when unparsing dict comprehension unpacking.
+Found by OSS Fuzz in :oss-fuzz:`489790200`.
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('}');
}