Commit a8683244 authored by Stefan Behnel's avatar Stefan Behnel

implement PEP 448 also for list/tuple literals

parent 0e6a7b7d
...@@ -15,8 +15,7 @@ Features added ...@@ -15,8 +15,7 @@ Features added
* Tracing is supported in ``nogil`` functions/sections and module init code. * Tracing is supported in ``nogil`` functions/sections and module init code.
* PEP 448 (Additional Unpacking Generalizations) was partially implemented * PEP 448 (Additional Unpacking Generalizations) was implemented.
for function calls, set and dict literals.
* When generators are used in a Cython module and the module imports the * When generators are used in a Cython module and the module imports the
modules "inspect" and/or "asyncio", Cython enables interoperability by modules "inspect" and/or "asyncio", Cython enables interoperability by
......
...@@ -5617,7 +5617,7 @@ class MergedDictNode(ExprNode): ...@@ -5617,7 +5617,7 @@ class MergedDictNode(ExprNode):
item.generate_disposal_code(code) item.generate_disposal_code(code)
item.free_temps(code) item.free_temps(code)
for helper in helpers: for helper in sorted(helpers):
code.globalstate.use_utility_code(UtilityCode.load_cached(helper, "FunctionArguments.c")) code.globalstate.use_utility_code(UtilityCode.load_cached(helper, "FunctionArguments.c"))
def annotate(self, code): def annotate(self, code):
...@@ -6161,14 +6161,14 @@ class AttributeNode(ExprNode): ...@@ -6161,14 +6161,14 @@ class AttributeNode(ExprNode):
# #
#------------------------------------------------------------------- #-------------------------------------------------------------------
class StarredTargetNode(ExprNode): class StarredUnpackingNode(ExprNode):
# A starred expression like "*a" # A starred expression like "*a"
# #
# This is only allowed in sequence assignment targets such as # This is only allowed in sequence assignment or construction such as
# #
# a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4] # a, *b = (1,2,3,4) => a = 1 ; b = [2,3,4]
# #
# and will be removed during type analysis (or generate an error # and will be special cased during type analysis (or generate an error
# if it's found at unexpected places). # if it's found at unexpected places).
# #
# target ExprNode # target ExprNode
...@@ -6177,17 +6177,22 @@ class StarredTargetNode(ExprNode): ...@@ -6177,17 +6177,22 @@ class StarredTargetNode(ExprNode):
is_starred = 1 is_starred = 1
type = py_object_type type = py_object_type
is_temp = 1 is_temp = 1
starred_expr_allowed_here = False
def __init__(self, pos, target): def __init__(self, pos, target):
ExprNode.__init__(self, pos) ExprNode.__init__(self, pos, target=target)
self.target = target
def analyse_declarations(self, env): def analyse_declarations(self, env):
error(self.pos, "can use starred expression only as assignment target") if not self.starred_expr_allowed_here:
error(self.pos, "starred expression is not allowed here")
self.target.analyse_declarations(env) self.target.analyse_declarations(env)
def infer_type(self, env):
return self.target.infer_type(env)
def analyse_types(self, env): def analyse_types(self, env):
error(self.pos, "can use starred expression only as assignment target") if not self.starred_expr_allowed_here:
error(self.pos, "starred expression is not allowed here")
self.target = self.target.analyse_types(env) self.target = self.target.analyse_types(env)
self.type = self.target.type self.type = self.target.type
return self return self
...@@ -6246,9 +6251,9 @@ class SequenceNode(ExprNode): ...@@ -6246,9 +6251,9 @@ class SequenceNode(ExprNode):
arg.analyse_target_declaration(env) arg.analyse_target_declaration(env)
def analyse_types(self, env, skip_children=False): def analyse_types(self, env, skip_children=False):
for i in range(len(self.args)): for i, arg in enumerate(self.args):
arg = self.args[i] if not skip_children:
if not skip_children: arg = arg.analyse_types(env) arg = arg.analyse_types(env)
self.args[i] = arg.coerce_to_pyobject(env) self.args[i] = arg.coerce_to_pyobject(env)
if self.mult_factor: if self.mult_factor:
self.mult_factor = self.mult_factor.analyse_types(env) self.mult_factor = self.mult_factor.analyse_types(env)
...@@ -6258,6 +6263,39 @@ class SequenceNode(ExprNode): ...@@ -6258,6 +6263,39 @@ class SequenceNode(ExprNode):
# not setting self.type here, subtypes do this # not setting self.type here, subtypes do this
return self return self
def _create_merge_node_if_necessary(self, env):
self._flatten_starred_args()
if not any(arg.is_starred for arg in self.args):
return self
# convert into MergedSequenceNode by building partial sequences
args = []
values = []
for arg in self.args:
if arg.is_starred:
if values:
args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
values = []
args.append(arg.target)
else:
values.append(arg)
if values:
args.append(TupleNode(values[0].pos, args=values).analyse_types(env, skip_children=True))
node = MergedSequenceNode(self.pos, args, self.type)
if self.mult_factor:
node = binop_node(
self.pos, '*', node, self.mult_factor.coerce_to_pyobject(env),
inplace=True, type=self.type, is_temp=True)
return node
def _flatten_starred_args(self):
args = []
for arg in self.args:
if arg.is_starred and arg.target.is_sequence_constructor and not arg.target.mult_factor:
args.extend(arg.target.args)
else:
args.append(arg)
self.args[:] = args
def may_be_none(self): def may_be_none(self):
return False return False
...@@ -6706,16 +6744,23 @@ class TupleNode(SequenceNode): ...@@ -6706,16 +6744,23 @@ class TupleNode(SequenceNode):
return self return self
if not skip_children: if not skip_children:
self.args = [arg.analyse_types(env) for arg in self.args] for i, arg in enumerate(self.args):
if not self.mult_factor and not any((arg.type.is_pyobject or arg.type.is_fused) for arg in self.args): if arg.is_starred:
arg.starred_expr_allowed_here = True
self.args[i] = arg.analyse_types(env)
if (not self.mult_factor and
not any((arg.is_starred or arg.type.is_pyobject or arg.type.is_fused) for arg in self.args)):
self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type self.type = env.declare_tuple_type(self.pos, (arg.type for arg in self.args)).type
self.is_temp = 1 self.is_temp = 1
return self return self
node = SequenceNode.analyse_types(self, env, skip_children=True) node = SequenceNode.analyse_types(self, env, skip_children=True)
if not all(child.is_literal for child in node.args): node = node._create_merge_node_if_necessary(env)
if not node.is_sequence_constructor:
return node return node
if not all(child.is_literal for child in node.args):
return node
if not node.mult_factor or ( if not node.mult_factor or (
node.mult_factor.is_literal and isinstance(node.mult_factor.constant_result, (int, long))): node.mult_factor.is_literal and isinstance(node.mult_factor.constant_result, (int, long))):
node.is_temp = False node.is_temp = False
...@@ -6822,6 +6867,9 @@ class ListNode(SequenceNode): ...@@ -6822,6 +6867,9 @@ class ListNode(SequenceNode):
return list_type return list_type
def analyse_expressions(self, env): def analyse_expressions(self, env):
for arg in self.args:
if arg.is_starred:
arg.starred_expr_allowed_here = True
node = SequenceNode.analyse_expressions(self, env) node = SequenceNode.analyse_expressions(self, env)
return node.coerce_to_pyobject(env) return node.coerce_to_pyobject(env)
...@@ -6833,6 +6881,7 @@ class ListNode(SequenceNode): ...@@ -6833,6 +6881,7 @@ class ListNode(SequenceNode):
release_errors(ignore=True) release_errors(ignore=True)
if env.is_module_scope: if env.is_module_scope:
self.in_module_scope = True self.in_module_scope = True
node = node._create_merge_node_if_necessary(env)
return node return node
def coerce_to(self, dst_type, env): def coerce_to(self, dst_type, env):
...@@ -7227,33 +7276,48 @@ class InlinedGeneratorExpressionNode(ScopedExprNode): ...@@ -7227,33 +7276,48 @@ class InlinedGeneratorExpressionNode(ScopedExprNode):
self.loop.generate_execution_code(code) self.loop.generate_execution_code(code)
class MergedSetNode(ExprNode): class MergedSequenceNode(ExprNode):
""" """
Merge a sequence of iterables into a set. Merge a sequence of iterables into a set/list/tuple.
The target collection is determined by self.type, which must be set externally.
args [SetNode or other ExprNode] args [ExprNode]
""" """
subexprs = ['args'] subexprs = ['args']
type = set_type
is_temp = True is_temp = True
gil_message = "Constructing Python set" gil_message = "Constructing Python collection"
def __init__(self, pos, args, type):
if type in (list_type, tuple_type) and args and args[0].is_sequence_constructor:
# construct a list directly from the first argument that we can then extend
if args[0].type is not list_type:
args[0] = ListNode(args[0].pos, args=args[0].args, is_temp=True)
ExprNode.__init__(self, pos, args=args, type=type)
def calculate_constant_result(self): def calculate_constant_result(self):
result = set() result = []
for item in self.args: for item in self.args:
if item.is_sequence_constructor and item.mult_factor: if item.is_sequence_constructor and item.mult_factor:
if item.mult_factor.constant_result <= 0: if item.mult_factor.constant_result <= 0:
continue continue
# otherwise, adding each item once should be enough
if item.is_set_literal or item.is_sequence_constructor: if item.is_set_literal or item.is_sequence_constructor:
# process items in order # process items in order
items = (arg.constant_result for arg in item.args) items = (arg.constant_result for arg in item.args)
else: else:
items = item.constant_result items = item.constant_result
result.update(items) result.extend(items)
if self.type is set_type:
result = set(result)
elif self.type is tuple_type:
result = tuple(result)
else:
assert self.type is list_type
self.constant_result = result self.constant_result = result
def compile_time_value(self, denv): def compile_time_value(self, denv):
result = set() result = []
for item in self.args: for item in self.args:
if item.is_sequence_constructor and item.mult_factor: if item.is_sequence_constructor and item.mult_factor:
if item.mult_factor.compile_time_value(denv) <= 0: if item.mult_factor.compile_time_value(denv) <= 0:
...@@ -7263,17 +7327,23 @@ class MergedSetNode(ExprNode): ...@@ -7263,17 +7327,23 @@ class MergedSetNode(ExprNode):
items = (arg.compile_time_value(denv) for arg in item.args) items = (arg.compile_time_value(denv) for arg in item.args)
else: else:
items = item.compile_time_value(denv) items = item.compile_time_value(denv)
result.extend(items)
if self.type is set_type:
try: try:
result.update(items) result = set(result)
except Exception as e: except Exception as e:
self.compile_time_value_error(e) self.compile_time_value_error(e)
elif self.type is tuple_type:
result = tuple(result)
else:
assert self.type is list_type
return result return result
def type_dependencies(self, env): def type_dependencies(self, env):
return () return ()
def infer_type(self, env): def infer_type(self, env):
return set_type return self.type
def analyse_types(self, env): def analyse_types(self, env):
args = [ args = [
...@@ -7283,10 +7353,12 @@ class MergedSetNode(ExprNode): ...@@ -7283,10 +7353,12 @@ class MergedSetNode(ExprNode):
for arg in self.args for arg in self.args
] ]
if len(args) == 1 and args[0].type is set_type: if len(args) == 1 and args[0].type is self.type:
# strip this intermediate node and use the bare set # strip this intermediate node and use the bare collection
return args[0] return args[0]
assert self.type in (set_type, list_type, tuple_type)
self.args = args self.args = args
return self return self
...@@ -7297,40 +7369,79 @@ class MergedSetNode(ExprNode): ...@@ -7297,40 +7369,79 @@ class MergedSetNode(ExprNode):
code.mark_pos(self.pos) code.mark_pos(self.pos)
self.allocate_temp_result(code) self.allocate_temp_result(code)
is_set = self.type is set_type
args = iter(self.args) args = iter(self.args)
item = next(args) item = next(args)
item.generate_evaluation_code(code) item.generate_evaluation_code(code)
if item.is_set_literal: if item.pos[1] == 140:
print item.type, item.dump()
if (is_set and item.is_set_literal or
not is_set and item.is_sequence_constructor and item.type is list_type):
code.putln("%s = %s;" % (self.result(), item.py_result())) code.putln("%s = %s;" % (self.result(), item.py_result()))
item.generate_post_assignment_code(code) item.generate_post_assignment_code(code)
else: else:
code.putln("%s = PySet_New(%s); %s" % ( code.putln("%s = %s(%s); %s" % (
self.result(), self.result(),
'PySet_New' if is_set else 'PySequence_List',
item.py_result(), item.py_result(),
code.error_goto_if_null(self.result(), self.pos))) code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.py_result()) code.put_gotref(self.py_result())
item.generate_disposal_code(code) item.generate_disposal_code(code)
item.free_temps(code) item.free_temps(code)
helpers = set()
if is_set:
add_func = "PySet_Add"
extend_func = "__Pyx_PySet_Update"
else:
add_func = "__Pyx_ListComp_Append"
extend_func = "__Pyx_PyList_Extend"
for item in args: for item in args:
if item.is_set_literal or (item.is_sequence_constructor and not item.mult_factor): if (is_set and (item.is_set_literal or item.is_sequence_constructor) or
(item.is_sequence_constructor and not item.mult_factor)):
if not is_set and item.args:
helpers.add(("ListCompAppend", "Optimize.c"))
for arg in item.args: for arg in item.args:
arg.generate_evaluation_code(code) arg.generate_evaluation_code(code)
code.put_error_if_neg(arg.pos, "PySet_Add(%s, %s)" % ( code.put_error_if_neg(arg.pos, "%s(%s, %s)" % (
add_func,
self.result(), self.result(),
arg.py_result())) arg.py_result()))
arg.generate_disposal_code(code) arg.generate_disposal_code(code)
arg.free_temps(code) arg.free_temps(code)
continue continue
if is_set:
helpers.add(("PySet_Update", "Builtins.c"))
else:
helpers.add(("ListExtend", "Optimize.c"))
item.generate_evaluation_code(code) item.generate_evaluation_code(code)
code.globalstate.use_utility_code(UtilityCode.load_cached("PySet_Update", "Builtins.c")) code.put_error_if_neg(item.pos, "%s(%s, %s)" % (
code.put_error_if_neg(item.pos, "__Pyx_PySet_Update(%s, %s)" % ( extend_func,
self.result(), self.result(),
item.py_result())) item.py_result()))
item.generate_disposal_code(code) item.generate_disposal_code(code)
item.free_temps(code) item.free_temps(code)
if self.type is tuple_type:
code.putln("{")
code.putln("PyObject *%s = PyList_AsTuple(%s);" % (
Naming.quick_temp_cname,
self.result()))
code.put_decref(self.result(), py_object_type)
code.putln("%s = %s; %s" % (
self.result(),
Naming.quick_temp_cname,
code.error_goto_if_null(self.result(), self.pos)))
code.put_gotref(self.result())
code.putln("}")
for helper in sorted(helpers):
code.globalstate.use_utility_code(UtilityCode.load_cached(*helper))
def annotate(self, code): def annotate(self, code):
for item in self.args: for item in self.args:
item.annotate(code) item.annotate(code)
......
...@@ -3833,6 +3833,93 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations): ...@@ -3833,6 +3833,93 @@ class ConstantFolding(Visitor.VisitorTransform, SkipDeclarations):
sequence_node.mult_factor = factor sequence_node.mult_factor = factor
return sequence_node return sequence_node
def visit_MergedDictNode(self, node):
"""Unpack **args in place if we can."""
self.visitchildren(node)
args = []
items = []
def add(arg):
if arg.is_dict_literal:
if items:
items[0].key_value_pairs.extend(arg.key_value_pairs)
else:
items.append(arg)
elif isinstance(arg, ExprNodes.MergedDictNode):
for child_arg in arg.keyword_args:
add(child_arg)
else:
if items:
args.append(items[0])
del items[:]
args.append(arg)
for arg in node.keyword_args:
add(arg)
if items:
args.append(items[0])
if len(args) == 1:
arg = args[0]
if arg.is_dict_literal or isinstance(arg, ExprNodes.MergedDictNode):
return arg
node.keyword_args[:] = args
self._calculate_const(node)
return node
def visit_MergedSequenceNode(self, node):
"""Unpack *args in place if we can."""
self.visitchildren(node)
is_set = node.type is Builtin.set_type
args = []
values = []
def add(arg):
if (is_set and arg.is_set_literal) or (arg.is_sequence_constructor and not arg.mult_factor):
if values:
values[0].args.extend(arg.args)
else:
values.append(arg)
elif isinstance(arg, ExprNodes.MergedSequenceNode):
for child_arg in arg.args:
add(child_arg)
else:
if values:
args.append(values[0])
del values[:]
args.append(arg)
for arg in node.args:
add(arg)
if values:
args.append(values[0])
if len(args) == 1:
arg = args[0]
if ((is_set and arg.is_set_literal) or
(arg.is_sequence_constructor and arg.type is node.type) or
isinstance(arg, ExprNodes.MergedSequenceNode)):
return arg
node.args[:] = args
self._calculate_const(node)
return node
def visit_SequenceNode(self, node):
"""Unpack *args in place if we can."""
self.visitchildren(node)
args = []
for arg in node.args:
if not arg.is_starred:
args.append(arg)
elif arg.target.is_sequence_constructor and not arg.target.mult_factor:
args.extend(arg.target.args)
else:
args.append(arg)
node.args[:] = args
self._calculate_const(node)
return node
def visit_PrimaryCmpNode(self, node): def visit_PrimaryCmpNode(self, node):
# calculate constant partial results in the comparison cascade # calculate constant partial results in the comparison cascade
self.visitchildren(node, ['operand1']) self.visitchildren(node, ['operand1'])
......
...@@ -210,7 +210,7 @@ def p_starred_expr(s): ...@@ -210,7 +210,7 @@ def p_starred_expr(s):
starred = False starred = False
expr = p_bit_expr(s) expr = p_bit_expr(s)
if starred: if starred:
expr = ExprNodes.StarredTargetNode(pos, expr) expr = ExprNodes.StarredUnpackingNode(pos, expr)
return expr return expr
def p_cascaded_cmp(s): def p_cascaded_cmp(s):
...@@ -915,11 +915,13 @@ def p_string_literal(s, kind_override=None): ...@@ -915,11 +915,13 @@ def p_string_literal(s, kind_override=None):
s.next() s.next()
return (kind, bytes_value, unicode_value) return (kind, bytes_value, unicode_value)
# list_display ::= "[" [listmaker] "]"
# listmaker ::= expression ( comp_for | ( "," expression )* [","] ) # since PEP 448:
# list_display ::= "[" [listmaker] "]"
# listmaker ::= (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
# comp_iter ::= comp_for | comp_if # comp_iter ::= comp_for | comp_if
# comp_for ::= "for" expression_list "in" testlist [comp_iter] # comp_for ::= "for" expression_list "in" testlist [comp_iter]
# comp_if ::= "if" test [comp_iter] # comp_if ::= "if" test [comp_iter]
def p_list_maker(s): def p_list_maker(s):
# s.sy == '[' # s.sy == '['
...@@ -927,24 +929,29 @@ def p_list_maker(s): ...@@ -927,24 +929,29 @@ def p_list_maker(s):
s.next() s.next()
if s.sy == ']': if s.sy == ']':
s.expect(']') s.expect(']')
return ExprNodes.ListNode(pos, args = []) return ExprNodes.ListNode(pos, args=[])
expr = p_test(s)
expr = p_test_or_starred_expr(s)
if s.sy == 'for': if s.sy == 'for':
if expr.is_starred:
s.error("iterable unpacking cannot be used in comprehension")
append = ExprNodes.ComprehensionAppendNode(pos, expr=expr) append = ExprNodes.ComprehensionAppendNode(pos, expr=expr)
loop = p_comp_for(s, append) loop = p_comp_for(s, append)
s.expect(']') s.expect(']')
return ExprNodes.ComprehensionNode( return ExprNodes.ComprehensionNode(
pos, loop=loop, append=append, type = Builtin.list_type, pos, loop=loop, append=append, type=Builtin.list_type,
# list comprehensions leak their loop variable in Py2 # list comprehensions leak their loop variable in Py2
has_local_scope = s.context.language_level >= 3) has_local_scope=s.context.language_level >= 3)
# (merged) list literal
if s.sy == ',':
s.next()
exprs = p_test_or_starred_expr_list(s, expr)
else: else:
if s.sy == ',': exprs = [expr]
s.next() s.expect(']')
exprs = p_simple_expr_list(s, expr) return ExprNodes.ListNode(pos, args=exprs)
else:
exprs = [expr]
s.expect(']')
return ExprNodes.ListNode(pos, args = exprs)
def p_comp_iter(s, body): def p_comp_iter(s, body):
if s.sy == 'for': if s.sy == 'for':
...@@ -1000,7 +1007,9 @@ def p_dict_or_set_maker(s): ...@@ -1000,7 +1007,9 @@ def p_dict_or_set_maker(s):
s.error("unexpected %sitem found in %s literal" % ( s.error("unexpected %sitem found in %s literal" % (
s.sy, 'set' if target_type == 1 else 'dict')) s.sy, 'set' if target_type == 1 else 'dict'))
s.next() s.next()
item = p_test(s) if s.sy == '*':
s.error("expected expression, found '*'")
item = p_starred_expr(s)
parts.append(item) parts.append(item)
last_was_simple_item = False last_was_simple_item = False
else: else:
...@@ -1058,9 +1067,6 @@ def p_dict_or_set_maker(s): ...@@ -1058,9 +1067,6 @@ def p_dict_or_set_maker(s):
for part in parts: for part in parts:
if isinstance(part, list): if isinstance(part, list):
set_items.extend(part) set_items.extend(part)
elif part.is_set_literal or part.is_sequence_constructor:
# unpack *{1,2,3} and *[1,2,3] in place
set_items.extend(part.args)
else: else:
if set_items: if set_items:
items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items)) items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items))
...@@ -1070,7 +1076,7 @@ def p_dict_or_set_maker(s): ...@@ -1070,7 +1076,7 @@ def p_dict_or_set_maker(s):
items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items)) items.append(ExprNodes.SetNode(set_items[0].pos, args=set_items))
if len(items) == 1 and items[0].is_set_literal: if len(items) == 1 and items[0].is_set_literal:
return items[0] return items[0]
return ExprNodes.MergedSetNode(pos, args=items) return ExprNodes.MergedSequenceNode(pos, args=items, type=Builtin.set_type)
else: else:
# (merged) dict literal # (merged) dict literal
items = [] items = []
...@@ -1078,9 +1084,6 @@ def p_dict_or_set_maker(s): ...@@ -1078,9 +1084,6 @@ def p_dict_or_set_maker(s):
for part in parts: for part in parts:
if isinstance(part, list): if isinstance(part, list):
dict_items.extend(part) dict_items.extend(part)
elif part.is_dict_literal:
# unpack **{...} in place
dict_items.extend(part.key_value_pairs)
else: else:
if dict_items: if dict_items:
items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items)) items.append(ExprNodes.DictNode(dict_items[0].pos, key_value_pairs=dict_items))
...@@ -1118,10 +1121,11 @@ def p_simple_expr_list(s, expr=None): ...@@ -1118,10 +1121,11 @@ def p_simple_expr_list(s, expr=None):
s.next() s.next()
return exprs return exprs
def p_test_or_starred_expr_list(s, expr=None): def p_test_or_starred_expr_list(s, expr=None):
exprs = expr is not None and [expr] or [] exprs = expr is not None and [expr] or []
while s.sy not in expr_terminators: while s.sy not in expr_terminators:
exprs.append( p_test_or_starred_expr(s) ) exprs.append(p_test_or_starred_expr(s))
if s.sy != ',': if s.sy != ',':
break break
s.next() s.next()
......
...@@ -16,6 +16,8 @@ def syntax1(): ...@@ -16,6 +16,8 @@ def syntax1():
[*a, *b] [*a, *b]
(a, b, *c, d, e, f, *g, h, i) (a, b, *c, d, e, f, *g, h, i)
[a, b, *c, d, e, f, *g, h, i]
{a, b, *c, d, e, f, *g, h, i}
def syntax2(): def syntax2():
...@@ -33,19 +35,15 @@ def types(l): ...@@ -33,19 +35,15 @@ def types(l):
_ERRORS = u""" _ERRORS = u"""
# syntax1() # syntax1()
8: 4: can use starred expression only as assignment target 8: 4: starred expression is not allowed here
10: 4: can use starred expression only as assignment target 10: 4: starred expression is not allowed here
12: 4: can use starred expression only as assignment target 12: 4: starred expression is not allowed here
14: 4: can use starred expression only as assignment target 14: 4: starred expression is not allowed here
16: 5: can use starred expression only as assignment target
16: 9: can use starred expression only as assignment target
18:11: can use starred expression only as assignment target
18:24: can use starred expression only as assignment target
# syntax2() # syntax2()
24:11: more than 1 starred expression in assignment 26:11: more than 1 starred expression in assignment
# types() # types()
30:15: Cannot coerce list to type 'int' 32:15: Cannot coerce list to type 'int'
31:10: starred target must have Python object (list) type 33:10: starred target must have Python object (list) type
""" """
...@@ -42,6 +42,7 @@ cdef object m(): ...@@ -42,6 +42,7 @@ cdef object m():
(x, y) (x, y)
[x, y] [x, y]
{x: y} {x: y}
{x, y}
obj and x obj and x
t(obj) t(obj)
# f(42) # Cython handles this internally # f(42) # Cython handles this internally
...@@ -129,35 +130,37 @@ _ERRORS = u""" ...@@ -129,35 +130,37 @@ _ERRORS = u"""
42:9: Discarding owned Python object not allowed without gil 42:9: Discarding owned Python object not allowed without gil
43:8: Constructing Python list not allowed without gil 43:8: Constructing Python list not allowed without gil
43:8: Discarding owned Python object not allowed without gil 43:8: Discarding owned Python object not allowed without gil
44:8: Constructing Python dict not allowed without gil 44:10: Constructing Python dict not allowed without gil
44:8: Discarding owned Python object not allowed without gil 44:10: Discarding owned Python object not allowed without gil
45:12: Discarding owned Python object not allowed without gil 45:10: Constructing Python set not allowed without gil
45:12: Truth-testing Python object not allowed without gil 45:10: Discarding owned Python object not allowed without gil
46:13: Python type test not allowed without gil 46:12: Discarding owned Python object not allowed without gil
48:10: Discarding owned Python object not allowed without gil 46:12: Truth-testing Python object not allowed without gil
48:10: Operation not allowed without gil 47:13: Python type test not allowed without gil
49:8: Discarding owned Python object not allowed without gil 49:10: Discarding owned Python object not allowed without gil
49:8: Operation not allowed without gil 49:10: Operation not allowed without gil
50:10: Assignment of Python object not allowed without gil 50:8: Discarding owned Python object not allowed without gil
50:14: Assignment of Python object not allowed without gil 50:8: Operation not allowed without gil
51:9: Assignment of Python object not allowed without gil 51:10: Assignment of Python object not allowed without gil
51:13: Assignment of Python object not allowed without gil 51:14: Assignment of Python object not allowed without gil
51:16: Creating temporary Python reference not allowed without gil 52:9: Assignment of Python object not allowed without gil
51:19: Creating temporary Python reference not allowed without gil 52:13: Assignment of Python object not allowed without gil
52:11: Assignment of Python object not allowed without gil 52:16: Creating temporary Python reference not allowed without gil
52:11: Indexing Python object not allowed without gil 52:19: Creating temporary Python reference not allowed without gil
53:11: Accessing Python attribute not allowed without gil
53:11: Assignment of Python object not allowed without gil 53:11: Assignment of Python object not allowed without gil
54:8: Constructing Python tuple not allowed without gil 53:11: Indexing Python object not allowed without gil
54:8: Python print statement not allowed without gil 54:11: Accessing Python attribute not allowed without gil
55:8: Deleting Python object not allowed without gil 54:11: Assignment of Python object not allowed without gil
56:8: Returning Python object not allowed without gil 55:8: Constructing Python tuple not allowed without gil
57:8: Raising exception not allowed without gil 55:8: Python print statement not allowed without gil
58:14: Truth-testing Python object not allowed without gil 56:8: Deleting Python object not allowed without gil
60:17: Truth-testing Python object not allowed without gil 57:8: Returning Python object not allowed without gil
62:8: For-loop using object bounds or target not allowed without gil 58:8: Raising exception not allowed without gil
62:14: Coercion from Python not allowed without the GIL 59:14: Truth-testing Python object not allowed without gil
62:25: Coercion from Python not allowed without the GIL 61:17: Truth-testing Python object not allowed without gil
64:8: Try-except statement not allowed without gil 63:8: For-loop using object bounds or target not allowed without gil
85:8: For-loop using object bounds or target not allowed without gil 63:14: Coercion from Python not allowed without the GIL
63:25: Coercion from Python not allowed without the GIL
65:8: Try-except statement not allowed without gil
86:8: For-loop using object bounds or target not allowed without gil
""" """
# mode: error
# tag: pep448
def unpack_mix():
[*1, **1]
_ERRORS = """
5:9: Expected an identifier or literal
"""
# mode: error
# tag: pep448
def unpack_wrong_stars():
[**1]
_ERRORS = """
5:5: Expected an identifier or literal
"""
# mode: error
# tag: pep448
def unpack_mix_in_set():
{*1, **2}
_ERRORS = """
5:9: unexpected **item found in set literal
"""
...@@ -23,10 +23,200 @@ class Map(object): ...@@ -23,10 +23,200 @@ class Map(object):
return self.mapping[key] return self.mapping[key]
#### tuples
@cython.test_fail_if_path_exists(
"//TupleNode//TupleNode",
"//MergedSequenceNode",
)
def unpack_tuple_literal():
"""
>>> unpack_tuple_literal()
(1, 2, 4, 5)
"""
return (*(1, 2, *(4, 5)),)
def unpack_tuple_literal_mult():
"""
>>> unpack_tuple_literal_mult()
(1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5)
"""
return (*((1, 2, *((4, 5) * 2)) * 3),)
@cython.test_fail_if_path_exists(
"//TupleNode//TupleNode",
"//MergedSequenceNode",
)
def unpack_tuple_literal_empty():
"""
>>> unpack_tuple_literal_empty()
()
"""
return (*(*(), *()), *(), *(*(*(),),))
def unpack_tuple_simple(it):
"""
>>> unpack_tuple_simple([])
()
>>> unpack_tuple_simple(set())
()
>>> unpack_tuple_simple(Iter())
()
>>> unpack_tuple_simple([1])
(1,)
>>> unpack_tuple_simple([2, 1])
(2, 1)
>>> unpack_tuple_simple((2, 1))
(2, 1)
>>> sorted(unpack_tuple_simple(set([2, 1])))
[1, 2]
>>> unpack_tuple_simple(Iter([2, 1]))
(2, 1)
"""
return (*it,)
def unpack_tuple_from_iterable(it):
"""
>>> unpack_tuple_from_iterable([1, 2, 3])
(1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3)
>>> unpack_tuple_from_iterable((1, 2, 3))
(1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3)
>>> sorted(unpack_tuple_from_iterable(set([1, 2, 3])))
[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
>>> unpack_tuple_from_iterable([1, 2])
(1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2)
>>> sorted(unpack_tuple_from_iterable(set([1, 2])))
[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2]
>>> unpack_tuple_from_iterable(Iter([1, 2]))
(1, 2, 1, 2, 1, 2, 1)
>>> unpack_tuple_from_iterable([3])
(1, 2, 3, 1, 3, 3, 3, 2, 1, 3)
>>> unpack_tuple_from_iterable(set([3]))
(1, 2, 3, 1, 3, 3, 3, 2, 1, 3)
>>> unpack_tuple_from_iterable(Iter([3]))
(1, 2, 3, 1, 2, 1)
>>> unpack_tuple_from_iterable([])
(1, 2, 1, 2, 1)
>>> unpack_tuple_from_iterable(set([]))
(1, 2, 1, 2, 1)
>>> unpack_tuple_from_iterable([])
(1, 2, 1, 2, 1)
>>> unpack_tuple_from_iterable(Iter([1, 2, 3]))
(1, 2, 1, 2, 3, 1, 2, 1)
"""
return (1, 2, *it, 1, *(*it, *it), *it, 2, 1, *it)
#### lists
@cython.test_fail_if_path_exists(
"//ListNode//ListNode",
"//MergedSequenceNode",
)
def unpack_list_literal():
"""
>>> unpack_list_literal()
[1, 2, 4, 5]
"""
return [*[1, 2, *[4, 5]]]
def unpack_list_literal_mult():
"""
>>> unpack_list_literal_mult()
[1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5, 1, 2, 4, 5, 4, 5]
"""
return [*([1, 2, *([4, 5] * 2)] * 3)]
@cython.test_fail_if_path_exists(
"//ListNode//ListNode",
"//MergedSequenceNode",
)
def unpack_list_literal_empty():
"""
>>> unpack_list_literal_empty()
[]
"""
return [*[*[], *[]], *[], *[*[*[]]]]
def unpack_list_simple(it):
"""
>>> unpack_list_simple([])
[]
>>> unpack_list_simple(set())
[]
>>> unpack_list_simple(Iter())
[]
>>> unpack_list_simple([1])
[1]
>>> unpack_list_simple([2, 1])
[2, 1]
>>> unpack_list_simple((2, 1))
[2, 1]
>>> sorted(unpack_list_simple(set([2, 1])))
[1, 2]
>>> unpack_list_simple(Iter([2, 1]))
[2, 1]
"""
return [*it]
def unpack_list_from_iterable(it):
"""
>>> unpack_list_from_iterable([1, 2, 3])
[1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3]
>>> unpack_list_from_iterable((1, 2, 3))
[1, 2, 1, 2, 3, 1, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 1, 1, 2, 3]
>>> sorted(unpack_list_from_iterable(set([1, 2, 3])))
[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3]
>>> unpack_list_from_iterable([1, 2])
[1, 2, 1, 2, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2]
>>> sorted(unpack_list_from_iterable(set([1, 2])))
[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2]
>>> unpack_list_from_iterable(Iter([1, 2]))
[1, 2, 1, 2, 1, 2, 1]
>>> unpack_list_from_iterable([3])
[1, 2, 3, 1, 3, 3, 3, 2, 1, 3]
>>> unpack_list_from_iterable(set([3]))
[1, 2, 3, 1, 3, 3, 3, 2, 1, 3]
>>> unpack_list_from_iterable(Iter([3]))
[1, 2, 3, 1, 2, 1]
>>> unpack_list_from_iterable([])
[1, 2, 1, 2, 1]
>>> unpack_list_from_iterable(set([]))
[1, 2, 1, 2, 1]
>>> unpack_list_from_iterable([])
[1, 2, 1, 2, 1]
>>> unpack_list_from_iterable(Iter([1, 2, 3]))
[1, 2, 1, 2, 3, 1, 2, 1]
"""
return [1, 2, *it, 1, *[*it, *it], *it, 2, 1, *it]
###### sets
@cython.test_fail_if_path_exists( @cython.test_fail_if_path_exists(
"//SetNode//SetNode", "//SetNode//SetNode",
"//MergedSetNode//SetNode", "//MergedSequenceNode",
"//MergedSetNode//MergedSetNode",
) )
def unpack_set_literal(): def unpack_set_literal():
""" """
...@@ -131,10 +321,12 @@ def unpack_set_from_iterable(it): ...@@ -131,10 +321,12 @@ def unpack_set_from_iterable(it):
return {1, 2, *it, 1, *{*it, *it}, *it, 2, 1, *it, *it} return {1, 2, *it, 1, *{*it, *it}, *it, 2, 1, *it, *it}
#### dicts
@cython.test_fail_if_path_exists( @cython.test_fail_if_path_exists(
"//DictNode//DictNode", "//DictNode//DictNode",
"//MergedDictNode//DictNode", "//MergedDictNode",
"//MergedDictNode//MergedDictNode",
) )
def unpack_dict_literal(): def unpack_dict_literal():
""" """
...@@ -145,6 +337,18 @@ def unpack_dict_literal(): ...@@ -145,6 +337,18 @@ def unpack_dict_literal():
return {**{'a': 1, 'b': 2, **{'c': 4, 'd': 5}}} return {**{'a': 1, 'b': 2, **{'c': 4, 'd': 5}}}
@cython.test_fail_if_path_exists(
"//DictNode//DictNode",
"//MergedDictNode",
)
def unpack_dict_literal_empty():
"""
>>> unpack_dict_literal_empty()
{}
"""
return {**{**{}, **{}}, **{}, **{**{**{}}}}
def unpack_dict_simple(it): def unpack_dict_simple(it):
""" """
>>> d = unpack_dict_simple({}) >>> d = unpack_dict_simple({})
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment