diff --git a/Doc/reference/simple_stmts.rst b/Doc/reference/simple_stmts.rst --- a/Doc/reference/simple_stmts.rst +++ b/Doc/reference/simple_stmts.rst @@ -110,31 +110,33 @@ decide about its validity, and may raise unacceptable. The rules observed by various types and the exceptions raised are given with the definition of the object types (see section :ref:`types`). .. index:: triple: target; list; assignment Assignment of an object to a target list, optionally enclosed in parentheses or square brackets, is recursively defined as follows. +* If the target list is empty: The object must also be an empty iterable. + * If the target list is a single target: The object is assigned to that target. * If the target list is a comma-separated list of targets: The object must be an iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets. * If the target list contains one target prefixed with an asterisk, called a - "starred" target: The object must be a sequence with at least as many items + "starred" target: The object must be a iterable with at least as many items as there are targets in the target list, minus one. The first items of the - sequence are assigned, from left to right, to the targets before the starred - target. The final items of the sequence are assigned to the targets after - the starred target. A list of the remaining items in the sequence is then + iterable are assigned, from left to right, to the targets before the starred + target. The final items of the iterable are assigned to the targets after + the starred target. A list of the remaining items in the iterable is then assigned to the starred target (the list can be empty). - * Else: The object must be a sequence with the same number of items as there + * Else: The object must be a iterable with the same number of items as there are targets in the target list, and the items are assigned, from left to right, to the corresponding targets. Assignment of an object to a single target is recursively defined as follows. * If the target is an identifier (name): * If the name does not occur in a :keyword:`global` or :keyword:`nonlocal` @@ -183,30 +185,30 @@ Assignment of an object to a single targ This description does not necessarily apply to descriptor attributes, such as properties created with :func:`property`. .. index:: pair: subscription; assignment object: mutable * If the target is a subscription: The primary expression in the reference is - evaluated. It should yield either a mutable sequence object (such as a list) + evaluated. It should yield either a mutable iterable object (such as a list) or a mapping object (such as a dictionary). Next, the subscript expression is evaluated. .. index:: - object: sequence + object: iterable object: list - If the primary is a mutable sequence object (such as a list), the subscript - must yield an integer. If it is negative, the sequence's length is added to + If the primary is a mutable iterable (such as a list), the subscript + must yield an integer. If it is negative, the iterable's length is added to it. The resulting value must be a nonnegative integer less than the - sequence's length, and the sequence is asked to assign the assigned object to + iterable's length, and the iterable is asked to assign the assigned object to its item with that index. If the index is out of range, :exc:`IndexError` is - raised (assignment to a subscripted sequence cannot add new items to a list). + raised (assignment to a subscripted iterable cannot add new items to a list). .. index:: object: mapping object: dictionary If the primary is a mapping object (such as a dictionary), the subscript must have a type compatible with the mapping's key type, and the mapping is then asked to create a key/datum pair which maps the subscript to the assigned @@ -214,35 +216,35 @@ Assignment of an object to a single targ value, or insert a new key/value pair (if no key with the same value existed). For user-defined objects, the :meth:`__setitem__` method is called with appropriate arguments. .. index:: pair: slicing; assignment * If the target is a slicing: The primary expression in the reference is - evaluated. It should yield a mutable sequence object (such as a list). The - assigned object should be a sequence object of the same type. Next, the lower + evaluated. It should yield a mutable iterable (such as a list). The + assigned object should be an iterable of the same type. Next, the lower and upper bound expressions are evaluated, insofar they are present; defaults - are zero and the sequence's length. The bounds should evaluate to integers. - If either bound is negative, the sequence's length is added to it. The - resulting bounds are clipped to lie between zero and the sequence's length, - inclusive. Finally, the sequence object is asked to replace the slice with - the items of the assigned sequence. The length of the slice may be different - from the length of the assigned sequence, thus changing the length of the - target sequence, if the target sequence allows it. + are zero and the iterable's length. The bounds should evaluate to integers. + If either bound is negative, the iterable's length is added to it. The + resulting bounds are clipped to lie between zero and the iterable's length, + inclusive. Finally, the iterable is asked to replace the slice with + the items of the assigned iterable. The length of the slice may be different + from the length of the assigned iterable, thus changing the length of the + target iterable, if the target iterable allows it. .. impl-detail:: In the current implementation, the syntax for targets is taken to be the same as for expressions, and invalid syntax is rejected during the code generation phase, causing less detailed error messages. Although the definition of assignment implies that overlaps between the -left-hand side and the right-hand side are 'simultanenous' (for example ``a, b = +left-hand side and the right-hand side are 'simultaneous' (for example ``a, b = b, a`` swaps two variables), overlaps *within* the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion. For instance, the following program prints ``[0, 2]``:: x = [0, 1] i = 0 i, x[i] = 1, 2 # i is updated, then x[i] is updated print(x) diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py --- a/Lib/test/test_codeop.py +++ b/Lib/test/test_codeop.py @@ -277,17 +277,16 @@ class CodeopTests(unittest.TestCase): ai("9+","eval") ai("lambda z:","eval") ai("a b","eval") ai("return 2.3") ai("if (a == 1 and b = 2): pass") ai("del 1") - ai("del ()") ai("del (1,)") ai("del [1]") ai("del '1'") ai("[i for i in range(10)] = (1, 2, 3)") def test_filename(self): self.assertEqual(compile_command("a = 1\n", "abc").co_filename, diff --git a/Lib/test/test_syntax.py b/Lib/test/test_syntax.py --- a/Lib/test/test_syntax.py +++ b/Lib/test/test_syntax.py @@ -30,24 +30,16 @@ Errors from set_context(): >>> obj.None = 1 Traceback (most recent call last): SyntaxError: invalid syntax >>> None = 1 Traceback (most recent call last): SyntaxError: can't assign to keyword -It's a syntax error to assign to the empty tuple. Why isn't it an -error to assign to the empty list? It will always raise some error at -runtime. - ->>> () = 1 -Traceback (most recent call last): -SyntaxError: can't assign to () - >>> f() = 1 Traceback (most recent call last): SyntaxError: can't assign to function call >>> del f() Traceback (most recent call last): SyntaxError: can't delete function call @@ -486,20 +478,16 @@ Make sure that the old "raise X, Y[, Z]" SyntaxError: invalid syntax >>> f(a=23, a=234) Traceback (most recent call last): ... SyntaxError: keyword argument repeated ->>> del () -Traceback (most recent call last): -SyntaxError: can't delete () - >>> {1, 2, 3} = 42 Traceback (most recent call last): SyntaxError: can't assign to literal Corner-cases that used to fail to raise the correct error: >>> def f(*, x=lambda __debug__:0): pass Traceback (most recent call last): diff --git a/Lib/test/test_unpack.py b/Lib/test/test_unpack.py --- a/Lib/test/test_unpack.py +++ b/Lib/test/test_unpack.py @@ -112,16 +112,37 @@ error) Trigger code while expecting an IndexError (unpack sequence too short, wrong error) >>> a, b, c = BadSeq() Traceback (most recent call last): ... test.test_unpack.BozoError +Allow unpacking empty iterables + + >>> () = [] + >>> [] = () + >>> [] = [] + >>> () = () + +Unpacking non-iterables should raise TypeError + + >>> () = 42 + Traceback (most recent call last): + ... + TypeError: 'int' object is not iterable + +Unpacking to an empty iterable should raise ValueError + + >>> () = [42] + Traceback (most recent call last): + ... + ValueError: too many values to unpack (expected 0) + """ __test__ = {'doctests' : doctests} def test_main(verbose=False): from test import support from test import test_unpack support.run_doctest(test_unpack, verbose) diff --git a/Lib/test/test_with.py b/Lib/test/test_with.py --- a/Lib/test/test_with.py +++ b/Lib/test/test_with.py @@ -135,21 +135,16 @@ class FailureTestCase(unittest.TestCase) self.assertRaises(SyntaxError, shouldRaiseSyntaxError, codestr) def testAssignmentToNoneError(self): self.assertRaisesSyntaxError('with mock as None:\n pass') self.assertRaisesSyntaxError( 'with mock as (None):\n' ' pass') - def testAssignmentToEmptyTupleError(self): - self.assertRaisesSyntaxError( - 'with mock as ():\n' - ' pass') - def testAssignmentToTupleOnlyContainingNoneError(self): self.assertRaisesSyntaxError('with mock as None,:\n pass') self.assertRaisesSyntaxError( 'with mock as (None,):\n' ' pass') def testAssignmentToTupleContainingNoneError(self): self.assertRaisesSyntaxError( diff --git a/Python/ast.c b/Python/ast.c --- a/Python/ast.c +++ b/Python/ast.c @@ -985,23 +985,18 @@ set_context(struct compiling *c, expr_ty } e->v.Name.ctx = ctx; break; case List_kind: e->v.List.ctx = ctx; s = e->v.List.elts; break; case Tuple_kind: - if (asdl_seq_LEN(e->v.Tuple.elts)) { - e->v.Tuple.ctx = ctx; - s = e->v.Tuple.elts; - } - else { - expr_name = "()"; - } + e->v.Tuple.ctx = ctx; + s = e->v.Tuple.elts; break; case Lambda_kind: expr_name = "lambda"; break; case Call_kind: expr_name = "function call"; break; case BoolOp_kind: