This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Assigning to subscript/slice of literal is permitted
Type: behavior Stage: resolved
Components: Versions: Python 3.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Isaac Elliott, r.david.murray, veky
Priority: normal Keywords:

Created on 2017-08-23 05:02 by Isaac Elliott, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (8)
msg300740 - (view) Author: Isaac Elliott (Isaac Elliott) Date: 2017-08-23 05:02
In Python 3.5 and 3.6 (at least), the language reference presents a grammar that disallows assignment to literals.

For example, `(a for 1 in [1,2,3])` is a syntax error, as is `(1, a) = (2, 3)`.

However the grammar doesn't prevent assignment to subscripted or sliced literals.

For example neither `(a for [1,2,3][0] in [1,2,3])` nor `([1,2,3][0], a) = (2, 3)` are considered syntax errors.

Similar behavior is exhibited for slices.

The problem is that the `target_list` production (https://docs.python.org/3.5/reference/simple_stmts.html#grammar-token-target_list) reuses the `subscription` and `slicing` productions which both use the `primary` production, allowing literals on their left side.
msg300750 - (view) Author: Vedran Čačić (veky) * Date: 2017-08-23 13:06
There is no such thing as "sliced literal" per se. And [1,2,3] is in fact _not_ a literal (it's a _list display_, at least it was the last time I learned Python's vocabulary.) [1,2,3][0] is an expression, which is a slice. When you write <whatever>[0] = 2, <whatever> is evaluated, and the result's __setitem__ is called. It is perfectly well-defined if the result is a list. Python doesn't care if it is a fresh list, or an already known one.

I guess it would be possible to change the grammar to disallow that, but do you really think it's worth it? And would you throw away also things such as [5,a][1][:]=[3]?
msg300754 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-08-23 14:52
I'm don't have a lot of experience with parsers, but I suspect that we consider the cost of making the grammar more complex to be more significant than the benefit we'd get from catching these at compile time.  And as Vedran says, defining what can be caught without breaking legitimate things is distinctly non-trivial.

I vote to just close this issue.  If you come up with a proposal we can reopen.
msg300766 - (view) Author: Isaac Elliott (Isaac Elliott) Date: 2017-08-23 22:21
Yes I would disallow a script such as
`a = [0]; [5, a][1][:] = [3]` (note: your example of just `[5, a][1][:] = [3]` does not run, so I assumed it must be used in a situation like this)

Evaluating the target of an assignment is unnecessary, we can syntactically determine whether some left hand side can be assigned to:

* Identifiers are assignable (`a = 2`)
* Attribute accesses are assignable, provided that the left of the dot is assignable (`a.foo = 2`, `a.b.c.foo`, etc)
* Subscripts are assignable, provided that the outer expression is assignable (`a[1] = 2`, `a.foo[b] = 2`, `a[1][2][3] = 2`, `a.b[1].c[2] = 2`)
* Lists are assignable, provided that all their elements are assignable (`[a,b,c] = [1,2,3]`)
* Expression lists/tuples are assignable, provided that all their elements are assignable (`a, b = (1, 2)`, `(a,b,c) = (1,2,3)`)
* Unpackings are assignable, provided that their argument is assignable (`*a, = [1,2,3]`, `a, *b = [1,2,3]`)
* Slices are assignable, provided that the outer expression is assignable (`a[:] = [1,2,3]`, `a.foo[1:2] = [1]`
* Everything else is not assignable (did I forget anything?)

This can definitely be encoded as a context-free grammar, although I don't know if it will present conflicts in the parser generator.

I do think it's worth it. Python is one of the most widely used programming languages, and it's our responsibility to ensure it behaves correctly.
msg300767 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-08-23 22:28
If you would disallow "a = [0]; [5, a][1][:] = [3]", then your proposal will not be accepted, for backward compatibility reasons if nothing else.
msg300768 - (view) Author: Isaac Elliott (Isaac Elliott) Date: 2017-08-23 22:34
Does backward compatibility take priority over correct behavior? What process is followed when fixing a bug causes a breaking change?
msg300774 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2017-08-24 03:21
Sometimes it does, sometimes we make the change in a feature release, often after a deprecation period.  But in this case there is doubt that the behavior is incorrect in the first place.

This discussion should move to the python-ideas mailing list.  I'm going to close the issue, at least for now.
msg300777 - (view) Author: Vedran Čačić (veky) * Date: 2017-08-24 06:57
I'm really curious about why exactly do you think that behavior is _incorrect_? Unfortunate, maybe. Similar to a mutable default argument. But it is completely consistent with Python semantics.
History
Date User Action Args
2022-04-11 14:58:51adminsetgithub: 75446
2017-08-24 06:57:28vekysetmessages: + msg300777
2017-08-24 03:21:15r.david.murraysetstatus: open -> closed
resolution: not a bug
messages: + msg300774

stage: resolved
2017-08-23 22:34:27Isaac Elliottsetmessages: + msg300768
2017-08-23 22:28:08r.david.murraysetmessages: + msg300767
versions: + Python 3.7, - Python 3.5, Python 3.6
2017-08-23 22:21:33Isaac Elliottsetmessages: + msg300766
2017-08-23 14:52:38r.david.murraysetnosy: + r.david.murray
messages: + msg300754
2017-08-23 13:06:36vekysetnosy: + veky
messages: + msg300750
2017-08-23 05:02:36Isaac Elliottcreate