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

bpo-42316: Document assignment expression need for ()s #23291

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

Copy link
Member

@terryjreedy terryjreedy commented Nov 15, 2020

The list should cover most places where an assignment sub-expression
must be wrapped in parentheses. Judging from the statement docs,
only a couple of statements do not require that they be wrapped.

https://bugs.python.org/issue42316

terryjreedy added 2 commits Nov 15, 2020
The list should cover most places where an assignment sub-expression
must be wrapped in parentheses. Judging from the statement docs,
only a couple of statements do not require that they be wrapped.
Assignment expressions must be surrounded by parentheses when used
as sub-expressions in subscript, slicing, conditional, lambda,
keyword-argument, generator, and comprehension-if expressions. They
can only be used as is in if and while statements and in decorators.
Copy link
Member Author

@terryjreedy terryjreedy Nov 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

" and in decorators" must be removed in the 3.8 backport.

@gvanrossum Have there been any other changes that you know of?

Copy link
Member

@gvanrossum gvanrossum Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. The plan was to look for other places where the parentheses could be omitted without confusing either humans or parsers, but we never did that, and I don't think there are any pressing needs.

Copy link
Member Author

@terryjreedy terryjreedy Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP-572 mentioned the extreme possibilities of requiring parentheses always and only where the parser required them, with the first rejected because if and while uses, as in the doc examples, were thought to be most common. For interactive use, augmented expression statements might be useful: a+= <complexe expression, to echo and bind to something other than _

Most of the sub-expressions requiring parentheses were pretty clearly better with them after I tried without. The most puzzling at the time is why (a:= i+j for j in range(3)) is not allowed while [a:= i+j for j in range(3)] is. If the ge is parenthesized, the parens for the ae look worse for me. But I can imagine that since the ()s are not part of the ge, a no-() ae might be a parsing problem. Even if not, this seems like a rare use since only the last value is accessible outside of the comprehension.

I recently used ass. expr.s for debugging (a use not mentioned in the PEP), to pull multiple values out a complex expression for a single debug print. Positional arguments were easy; indexes required ()s. The latter is probably the first extension of 'no () sub-expressions' I would want looked at.

Copy link
Member

@gvanrossum gvanrossum Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it makes sense for a[i := f()] and for a[i:= f(), j := g()] etc., though I would prefer to require the parentheses in slices (too many colons otherwise :-). Provided we can exclude it in slices, it should be a simple change to the grammar to support it in indexes. Maybe @lysnikolaou can help?

Copy link
Contributor

@lysnikolaou lysnikolaou Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, that's a very simple change to the parser. I'll have a PR ready later tonight.

Assignment expressions must be surrounded by parentheses when used
as sub-expressions in subscript, slicing, conditional, lambda,
keyword-argument, generator, and comprehension-if expressions. They
can only be used as is in if and while statements and in decorators.
Copy link
Member

@gvanrossum gvanrossum Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
can only be used as is in if and while statements and in decorators.
can only be used unparenthesized in if and while statements and in decorators.

Copy link
Member Author

@terryjreedy terryjreedy Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. In addition, a list of unparenthesized subexpressions would be shorter: positional arguments in calls and single-value-expressions in set and list displays, including comprehensions, and parenthesized tuples. (Since ()s are not part of the tuple, the difference with ()s surprises me.) Am incomplete list is still safe.

Copy link
Member

@gvanrossum gvanrossum Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you just apply this so we can land it? You can file a new issue to allow more scenarios.

Also, can you double check that the difference for () and [] is not a regression in the new parser?

Copy link
Contributor

@lysnikolaou lysnikolaou Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is indeed a regression. I'll have a look at this as well.

Copy link
Member Author

@terryjreedy terryjreedy Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this in 3.8.6 as well as 3.10, so it is an oddity but not a regression.

>>> (a:=1, b:=2)
(1, 2)
>>> a:=1, b:=2
SyntaxError: invalid syntax

Copy link
Member

@gvanrossum gvanrossum Nov 17, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a different thing that what I was asking about -- I was asking about

[a := i for i in ...]
(a := i for i in ...)

The former is valid in 3.8 through 3.10, the latter in 3.8 but not in later versions (until @lysnikolaou fixed it earlier today :-). So that's definitely a regression.

The thing you show is due to a generalization of the rule that := is not allowed at the top level:

>>> (a := 1)
1
>>> a := 1
  File "<stdin>", line 1
    a := 1
          ^
SyntaxError: invalid syntax

I wouldn't want to change that.

@lysnikolaou
Copy link

@lysnikolaou lysnikolaou commented Nov 16, 2020

This PR probably needs to be updated to reflect the recent changes introduced in #23317 and #23319. Sorry @terryjreedy!

@terryjreedy
Copy link
Author

@terryjreedy terryjreedy commented Nov 17, 2020

These are changes I hoped to be able to make ;-). I will add the new in 3.10 item for indexes.

@github-actions
Copy link

@github-actions github-actions bot commented Dec 18, 2020

This PR is stale because it has been open for 30 days with no activity.

Copy link
Member

@JelleZijlstra JelleZijlstra left a comment

@terryjreedy are you still interested in landing this? A few older comments were never applied.

@@ -1745,6 +1745,11 @@ Or, when processing a file stream in chunks:
while chunk := file.read(9000):
process(chunk)
Assignment expressions must be surrounded by parentheses when used
as sub-expressions in subscript, slicing, conditional, lambda,
Copy link
Member

@JelleZijlstra JelleZijlstra Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first one (subscripts) isn't true in 3.11:

>>> a = [1, 2, 3]
>>> a[x:=1]
2
>>> x
1

@@ -1745,6 +1745,11 @@ Or, when processing a file stream in chunks:
while chunk := file.read(9000):
process(chunk)
Assignment expressions must be surrounded by parentheses when used
as sub-expressions in subscript, slicing, conditional, lambda,
keyword-argument, generator, and comprehension-if expressions. They
Copy link
Member

@JelleZijlstra JelleZijlstra Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

genexps don't need parens in 3.11:

>>> (x:=3 for y in [])
<generator object <genexpr> at 0x102ae97d0>

They completely disallow assignment expressions for the iterable (not sure why):

>>> (y for y in (z:=[]))
  File "<stdin>", line 1
SyntaxError: assignment expression cannot be used in a comprehension iterable expression
>>> (y for y in z:=[])
  File "<stdin>", line 1
    (y for y in z:=[])
                 ^^
SyntaxError: invalid syntax

Copy link
Member

@gvanrossum gvanrossum Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's explained somewhere in PEP 572. "While it’s technically possible to assign consistent semantics to these cases, it’s difficult to determine whether those semantics actually make sense in the absence of real use cases."

Copy link
Member

@JelleZijlstra JelleZijlstra Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh interesting, I think it's actually this part a few paragraphs down: "Due to design constraints in the reference implementation (the symbol table analyser cannot easily detect when names are re-used between the leftmost comprehension iterable expression and the rest of the comprehension), named expressions are disallowed entirely as part of comprehension iterable expressions (the part after each “in”, and before any subsequent “if” or “for” keyword)"

Copy link
Member

@gvanrossum gvanrossum Apr 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And so yet another wart in the specification was born... :-(

@terryjreedy terryjreedy removed their assignment Apr 6, 2022
@terryjreedy
Copy link
Author

@terryjreedy terryjreedy commented Apr 6, 2022

I somehow missed finishing this and would like it updated and merged. The doc is needed; someone just posted code to pydev that was missing required ()s. However, I just discovered that I have been recently de-authorized from merging anything. Jelle, you have been doing great with doc patches, so it would be fine with me if you finished this without waiting for that problem to be fixed.

@JelleZijlstra
Copy link

@JelleZijlstra JelleZijlstra commented Apr 6, 2022

I have been recently de-authorized from merging anything

Pablo had turned off merges for everyone during the 3.11.0a7 release, but merges are allowed now again.

@JelleZijlstra JelleZijlstra self-assigned this Apr 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
awaiting core review CLA signed docs needs backport to 3.9 stale
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants