-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[syntax-errors] Parenthesized context managers before Python 3.9 #16523
base: main
Are you sure you want to change the base?
Conversation
Summary -- WIP currently I just added all of the valid cases from Alex's comment here #16106 (comment) Test Plan -- Inline tests
|
// test_err parenthesized_context_manager_py38 | ||
// # parse_options: {"target-version": "3.8"} | ||
// with (foo as x, bar as y): ... | ||
self.add_unsupported_syntax_error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we also need to emit the same diagnostic on line 2144, e.g. for with (foo, bar):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, that syntax is actually okay. I think (foo, bar)
is just a tuple, so it falls under the expression grammar. The tuple doesn't have an __enter__
method, so it causes a runtime error, but it does parse and even compile I guess.
As far as I can tell, the problem with parentheses is specific to the as
case since that's part of the with
statement grammar.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Python 3.7.9 (default, Aug 23 2020, 00:57:53)
[Clang 10.0.1 ] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ast
>>> ast.parse("with (foo, bar): ...")
<_ast.Module object at 0x70849fd20c10>
>>> ast.dump(_)
"Module(body=[With(items=[withitem(context_expr=Tuple(elts=[Name(id='foo', ctx=Load()), Name(id='bar', ctx=Load())], ctx=Load()), optional_vars=None)], body=[Expr(value=Ellipsis())])])"
@@ -536,6 +536,7 @@ pub enum UnsupportedSyntaxErrorKind { | |||
TypeParameterList, | |||
TypeAliasStatement, | |||
TypeParamDefault, | |||
ParenthesizedContextManager, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be nice to add documentation, similar to what you did for other variants.
// test_err parenthesized_context_manager_py38 | ||
// # parse_options: {"target-version": "3.8"} | ||
// with (foo as x, bar as y): ... | ||
self.add_unsupported_syntax_error( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we move this check down below where we're sure that the parenthesis belongs to the with items and not the expression? i.e., on line 2162
if with_item_kind.is_parenthesized() {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, in parse_with_items
like:
if self.at(TokenKind::Lpar) {
let lpar_range = self.current_token_range();
if let Some(items) = self.try_parse_parenthesized_with_items() {
// check target version and raise error
self.expect(TokenKind::Rpar);
items
} else {
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh nice, I moved it to parse_with_items
. I just had to add a separate check for optional_vars
otherwise we get a false positive on the case Micha mentioned above. I added that as a test_ok
case to be sure.
Thanks for the reviews! I
|
Summary
I thought this was very complicated based on the comment here: #16106 (comment) and on some of the discussion in the CPython issue here: python/cpython#56991. However, after a little bit of experimentation, I think it boils down to this example:
The issue is parentheses around a
with
item with anoptional_var
, as we (and Python) call the trailing variable name (y
in this case). It's not actually about line breaks after all, except that line breaks are allowed in parenthesized expressions, which explains the validity of cases likeeven on Python 3.8.
I followed pyright's example again here on the diagnostic range (just the opening paren) and the wording of the error.
Test Plan
Inline tests