Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can assign [] = (), but not () = [] #67464

Closed
ssbr mannequin opened this issue Jan 19, 2015 · 31 comments
Closed

Can assign [] = (), but not () = [] #67464

ssbr mannequin opened this issue Jan 19, 2015 · 31 comments
Assignees
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@ssbr
Copy link
Mannequin

ssbr mannequin commented Jan 19, 2015

BPO 23275
Nosy @rhettinger, @amauryfa, @mdickinson, @ncoghlan, @scoder, @ssbr, @ezio-melotti, @bitdancer, @flying-sheep, @berkerpeksag, @vadmium, @eryksun
Files
  • issue23275.diff
  • issue23275_v2.diff
  • issue23275_v3.diff
  • issue23275_v4.diff
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = 'https://github.com/vadmium'
    closed_at = <Date 2016-05-18.05:48:24.849>
    created_at = <Date 2015-01-19.19:05:47.809>
    labels = ['interpreter-core', 'type-feature']
    title = 'Can assign [] = (), but not () = []'
    updated_at = <Date 2016-06-08.13:36:22.613>
    user = 'https://github.com/ssbr'

    bugs.python.org fields:

    activity = <Date 2016-06-08.13:36:22.613>
    actor = 'python-dev'
    assignee = 'martin.panter'
    closed = True
    closed_date = <Date 2016-05-18.05:48:24.849>
    closer = 'berker.peksag'
    components = ['Interpreter Core']
    creation = <Date 2015-01-19.19:05:47.809>
    creator = 'Devin Jeanpierre'
    dependencies = []
    files = ['39165', '39703', '42715', '42878']
    hgrepos = []
    issue_num = 23275
    keywords = ['patch']
    message_count = 31.0
    messages = ['234324', '234325', '234326', '234361', '234364', '234365', '241473', '241474', '241523', '241673', '241790', '241792', '244157', '244159', '244161', '244208', '244215', '244227', '245308', '246382', '264223', '264226', '264249', '264811', '265108', '265764', '265809', '265811', '265816', '265817', '267847']
    nosy_count = 17.0
    nosy_names = ['rhettinger', 'amaury.forgeotdarc', 'mark.dickinson', 'ncoghlan', 'scoder', 'Devin Jeanpierre', 'ezio.melotti', 'ionelmc', 'r.david.murray', 'flying sheep', 'python-dev', 'berker.peksag', 'martin.panter', 'eryksun', 'Cesar.Kawakami', 'Kyle.Buzsaki', 'Rahul Gupta']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue23275'
    versions = ['Python 3.6']

    @ssbr
    Copy link
    Mannequin Author

    ssbr mannequin commented Jan 19, 2015

    >>> [] = ()
    >>> () = []
      File "<stdin>", line 1
    SyntaxError: can't assign to ()

    This contradicts the assignment grammar, which would make both illegal: https://docs.python.org/3/reference/simple_stmts.html#assignment-statements

    @ssbr ssbr mannequin added type-bug An unexpected behavior, bug, or error interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Jan 19, 2015
    @bitdancer
    Copy link
    Member

    My guess is that it is not worth complicating the parser in order to make these two cases consistent, and it should be treated as a doc error. We'll see what other developers think.

    @rhettinger
    Copy link
    Contributor

    The starting point is recognizing that this has been around for very long time and is harmless.

    @KyleBuzsaki
    Copy link
    Mannequin

    KyleBuzsaki mannequin commented Jan 20, 2015

    It seems that assigning to [] is the odd one out in this case. Why is this even possible?

    >>> [] = ()
    >>> [] = {}
    >>> [] = set()
    >>> list() = ()
      File "<stdin>", line 1
    SyntaxError: can't assign to function call
    >>> () = []
      File "<stdin>", line 1
    SyntaxError: can't assign to ()
    >>> {} = []
      File "<stdin>", line 1
    SyntaxError: can't assign to literal
    >>> set() = []
      File "<stdin>", line 1
    SyntaxError: can't assign to function call
    >>>

    @vadmium
    Copy link
    Member

    vadmium commented Jan 20, 2015

    But () is the odd one out if you consider

    >>> [a, b] = range(2)
    >>> [] = range(0)
    >>> (a, b) = range(2)
    >>> () = range(0)
      File "<stdin>", line 1
    SyntaxError: can't assign to ()

    @eryksun
    Copy link
    Contributor

    eryksun commented Jan 20, 2015

    In ast.c, set_context checks for assignment to an empty tuple, but not an empty list.

            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 = "()";
                }
                break;

    https://hg.python.org/cpython/file/ab2c023a9432/Python/ast.c#l912

    @ncoghlan
    Copy link
    Contributor

    As Raymond notes, this is a fairly harmless quirk - it changes a SyntaxError to an iterable length dependent ValueError:

    >>> () = []
      File "<stdin>", line 1
    SyntaxError: can't assign to ()
    >>> [] = ()
    >>> [] = [1]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    ValueError: too many values to unpack (expected 0)

    I only found out about this after being puzzled when a typo in a live demo at PyCon 2015 failed to fail as I expected after seeing the presenter type a "[]" into the LHS of an assignment instead of the intended "_".

    Removing the data dependence to make the assignment fail immediately (even if never tested against a non-empty iterable) would involve making the handling of List_kind match that of Tuple_kind in the switch statement that eryksun quoted.

    I don't think it's an urgent fix, but if someone wanted to fix it (including a new test), I think it would be a reasonable contribution to accept.

    @vadmium
    Copy link
    Member

    vadmium commented Apr 19, 2015

    I would prefer this be fixed in the opposite direction, to allow “unpacking” an empty iterable using round brackets. I have used this syntax on purpose as a concise way to ensure that a generator is exhaused with no more yields:

    >>> def gen():
    ...     yield "partial computation"
    ...     print("computation allowed to complete")
    ... 
    >>> g = gen()
    >>> next(g)
    'partial computation'
    >>> [] = g
    computation allowed to complete

    @bitdancer
    Copy link
    Member

    There is also no reason to break currently working code, which turning this into a syntax error would od.

    @mdickinson
    Copy link
    Member

    There is also no reason to break currently working code

    Agreed. To take one example, David Beazley's PyCon 2015 talk would have been broken by the suggested change! (See https://www.youtube.com/watch?v=MCs5OvhV9S4, at around the 42:17 mark.)

    If there's any code change resulting from this issue, I also think it should be to make assignment to () legal.

    @berkerpeksag
    Copy link
    Member

    I don't have a strong opinion on this, but here is a patch to make () = [] a valid assignment.

    @amauryfa
    Copy link
    Member

    About the patch: I'm sure there are other tests to change,
    in test_syntax.py for example::

    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):
          File "<doctest test.test_syntax[3]>", line 1
        SyntaxError: can't assign to ()

    @RahulGupta
    Copy link
    Mannequin

    RahulGupta mannequin commented May 27, 2015

    isn't it logical?

    [] is a mutable data structure
    while () is a immutable data structure

    (b, a) = [1, 2] is fine because a and b are mutable

    @ssbr
    Copy link
    Mannequin Author

    ssbr mannequin commented May 27, 2015

    [a, b] = (1, 2) is also fine.

    @vadmium
    Copy link
    Member

    vadmium commented May 27, 2015

    I prefer to unpack into square brackets in general because it is a mnemonic for the star argument being a list:

    >>> (a, *b) = range(3)
    >>> a
    0
    >>> b  # A list, even though it was unpacked using tuple-like syntax
    [1, 2]

    @rhettinger
    Copy link
    Contributor

    Berker's patch looks good.
    It has several virtues:

    • the error message is reasonable and clear
    • it makes the language more consistent
    • it doesn't break any existing code.
    • it makes the AST a little simpler and faster
      by removing a special case

    The patch needs to updated:

    • remove the whatsnew entry
    • fix test_codeop which expects "del ()" to raise SyntaxError
    • fix test_syntax which fails on "() = 1" and "del ()"
    • fix test_with which fails on "with mock as ()"

    @rhettinger rhettinger self-assigned this May 27, 2015
    @flying-sheep
    Copy link
    Mannequin

    flying-sheep mannequin commented May 27, 2015

    isn't it logical?

    [] is a mutable data structure
    while () is a immutable data structure

    but you don’t assign to data structures, but to names. you *modify* data structures. and in the square bracket assignment syntax you don’t modify the list created by the []. in fact the [] never even create a list.

    —————————————————————————————

    also it’s news to me that [a, b] = range(2) works!

    i always did a, b = range(2), and knew that (a, b) = range(2) works.

    but assigning to something looking like a list literal is new and surprising to me. (and i thought i’ve mastered every corner of python’s syntax)

    @ncoghlan
    Copy link
    Contributor

    +1 for Martin's suggestion of removing the inconsistency the other way (i.e. allowing "() = iterable" to mean the same thing as "[] = iterable", effectively asserting that an iterable is empty)

    I also agree with Raymond that it doesn't need to be mentioned in the What's New doc, just in the NEWS file.

    @berkerpeksag
    Copy link
    Member

    Thanks for the reviews. Here is an updated patch.

    @vadmium
    Copy link
    Member

    vadmium commented Jul 6, 2015

    I welcome this patch to go ahead. But the documentation <https://docs.python.org/3.6/reference/simple_stmts.html#assignment-statements\> also needs updating (see original post). I think it should mention that “target_list” can be empty. And it should use “iterable” instead of “sequence” in more places.

    @rhettinger
    Copy link
    Contributor

    Martin, do you want to take it from here?

    @rhettinger rhettinger assigned vadmium and unassigned rhettinger Apr 26, 2016
    @berkerpeksag
    Copy link
    Member

    I missed Martin's comment about the documentation. I will update my patch.

    @vadmium
    Copy link
    Member

    vadmium commented Apr 26, 2016

    Okay I’ll let Berker update his patch, but I’m happy to try my hand at updating the documentation if needed.

    I reviewed the current patch. I can’t say whether the ast.c change is good or not. But IMO the test would be better off in somewhere like /Lib/test/test_unpack.py. It is only a superficial relationship with tuples because the syntax looks the same. Also may be worth testing that [] = [] etc work as well.

    @berkerpeksag
    Copy link
    Member

    Here is an updated patch. I moved the test into test_unpack and added additional tests. sequence -> iterable changes should probably be applied to 3.5 as well.

    Thanks for the review, Martin.

    @berkerpeksag berkerpeksag removed the type-bug An unexpected behavior, bug, or error label May 4, 2016
    @berkerpeksag berkerpeksag added the type-feature A feature request or enhancement label May 4, 2016
    @vadmium
    Copy link
    Member

    vadmium commented May 8, 2016

    Erm, I think you went overboard with the sequence → iterable changes and subscripting; see the review. Also, I think target_list should be made optional in the grammar description.

    @vadmium
    Copy link
    Member

    vadmium commented May 17, 2016

    Hi Berker. I updated your patch according to my comments in the documentation. I hope you don’t mind.

    I reverted all the changes about subscripting and slicing an iterable, since this bug is only about assigning to a “target list”.

    Actually it is true (despite the current documentation) that you can often assign

    sequence[slice] = iterable

    But I think that is a separate problem.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented May 18, 2016

    New changeset 8a0754fed986 by Berker Peksag in branch 'default':
    Issue bpo-23275: Allow () = iterable assignment syntax
    https://hg.python.org/cpython/rev/8a0754fed986

    @berkerpeksag
    Copy link
    Member

    Thanks Martin. Your edits look much better! :) I will be travelling for PyCon US later this week so I just committed issue23275_v4.diff with minor edits.

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented May 18, 2016

    New changeset d3a75daf61e1 by Martin Panter in branch 'default':
    Issue bpo-23275: Don’t think this made it into alpha 1
    https://hg.python.org/cpython/rev/d3a75daf61e1

    @vadmium
    Copy link
    Member

    vadmium commented May 18, 2016

    I just moved the NEWS entry under the alpha 2 heading.

    For patches I am going to commit myself, I usually write the NEWS beforehand and remember to adjust it when committing. But perhaps I shouldn’t have done that in this case :)

    @python-dev
    Copy link
    Mannequin

    python-dev mannequin commented Jun 8, 2016

    New changeset 39a72018dd76 by Martin Panter in branch '3.5':
    Issue bpo-23275: Backport target list assignment documentation fixes
    https://hg.python.org/cpython/rev/39a72018dd76

    New changeset 8700f4d09b28 by Martin Panter in branch '2.7':
    Issue bpo-23275: Backport empty square bracket assignment documentation fix
    https://hg.python.org/cpython/rev/8700f4d09b28

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    8 participants