-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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] Named expressions in decorators before Python 3.9 #16386
Conversation
91d9f95
to
31fe82e
Compare
|
d8231f5
to
f73d2b2
Compare
31fe82e
to
894a296
Compare
CodSpeed Performance ReportMerging #16386 will not alter performanceComparing Summary
|
894a296
to
5801f20
Compare
6cda055
to
3c98d84
Compare
5801f20
to
c833f0c
Compare
c833f0c
to
e57b11e
Compare
e57b11e
to
ca5b34f
Compare
@@ -457,6 +458,7 @@ impl Display for UnsupportedSyntaxError { | |||
UnsupportedSyntaxErrorKind::Match => "`match` statement", | |||
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)", | |||
UnsupportedSyntaxErrorKind::ExceptStar => "`except*`", | |||
UnsupportedSyntaxErrorKind::RelaxedDecorator => "named expression in decorator", |
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.
Hmm, I think "named expression" generally refers specifically to walrus expressions (https://peps.python.org/pep-0572 uses the term several times), so I'm not sure it's the best term to use here
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.
Yeah that makes sense, I was just following PEP 614 and the AST reference (which I see now actually calls them assignment_expression
s, but I think that's another synonym for the walrus operator?).
Do you have any ideas for a replacement? I didn't think "relaxed decorator" was much more helpful, but that's closer to the PEP name and the "What's new" entry.
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.
Maybe something like this?
impl Display for UnsupportedSyntaxError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let kind = match self.kind {
UnsupportedSyntaxErrorKind::Match => "`match` statement",
UnsupportedSyntaxErrorKind::Walrus => "named assignment expression (`:=`)",
UnsupportedSyntaxErrorKind::ExceptStar => "`except*`",
UnsupportedSyntaxErrorKind::RelaxedDecorator => {
let expression_kind = ...;
return write!(
"Cannot use {expression_kind} expressions in decorators on Python {} (syntax was added in Python 3.9)",
self.target_version,
);
}
};
write!(
f,
"Cannot use {kind} on Python {} (syntax was added in Python {})",
self.target_version,
self.kind.minimum_version(),
)
}
}
Then if it was e.g. a subscript expression, the error message would be
Cannot use subscript expressions in decorators on Python 3.8 (syntax was added in Python 3.9)
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.
I think I'll leave this nicer error reporting for a possible follow-up. It would complicate the code a fair amount to track the expression kind through the whole process just for the error message. Micha also pointed out on Discord that 3.9 is the oldest version receiving security updates, so this message will probably have a fairly limited audience.
I did remove the confusing "named expression" part and went for something closer to pyright's "Expression form not supported for decorator prior to Python 3.9":
Syntax Error: Unsupported expression in decorators on Python 3.8 (syntax was added in Python 3.9)
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.
Nice, thanks. I still think there's room for improvement but this is definitely good enough for now!
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.
Absolutely, I meant to say I think your suggestion would make for the ideal error message. Thanks!
Is it possible that this one would be easier to detect using the "greeter" approach that you proposed for syntax errors emitted by the compiler? |
Hmm, maybe it would be slightly easier. We could store state indicating that we're in a decorator and then check if each visited component of the expression is an attribute access, with an optional top-level Still, I think the approach here makes sense (at least to me 😆) and lets us keep the detection in the parser. It's just certainly trickier than some of the other errors like "is this a walrus operator." And I wasn't sure if I wrote idiomatic parser code or if this is exactly what the checkpoint system is meant for. |
@@ -2554,14 +2626,34 @@ impl<'src> Parser<'src> { | |||
let decorator_start = self.node_start(); | |||
self.bump(TokenKind::At); | |||
|
|||
let checkpoint = self.checkpoint(); |
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.
I'd prefer not to use checkpoints here. Checkpoints are expensive.
I think I'd prefer to visit the parsed AST instead and check if any name isn't a named expression. One thing that might be challenging is that the outer-most expression can't be parenthesized, but that's something that we should be able to detect at the parse decorator function
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.
I don't think it's just named expression. As per the grammar and the tests show this as well, one other example is a subscript expression like @foo[0]
(top-level expression is subscript) or @foo[0].bar
(top-level expression is an attribute) as both are not allowed.
I agree that it might be simplest to do this using a visitor instead of using speculative parsing.
I quickly looked at what Pyright does and it seems to be doing a basic check on the parsed expression if the target version is < 3.9: https://github.com/microsoft/pyright/blob/1d1b43c17a83927d422c9ff52f98fb595419e329/packages/pyright-internal/src/parser/parser.ts#L2401-L2417. I'm not sure if that covers the entire extent of the grammar but it might be worth exploring.
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 looks like pyright's _isNameOrMemberAccessExpression
function called there is recursively checking the decorator expression. I guess that is a minimal visitor. When Alex mentioned the greeter approach, I was picturing holding off on this error until I start working on the separate greeter for compile-time/semantic errors. Should I just have a mini visitor directly in the parser like pyright instead?
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.
I don't mind a mini visitor in the parser, as long as it isn't a full Visitor
based visitor (and only runs for < 3.9)
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.
Of course, I think I can pretty much follow pyright directly here.
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.
Thanks for looking into it. I also think that's fine.
It might be interesting to add a test that uses a walrus expression in a decorator with the target version set to py37 |
this also removes `@` from the diagnostic, also like pyright
…ator syntax errors (#16581) ## Summary A small followup to #16386. We now tell the user exactly what it was about their decorator that constituted invalid syntax on Python <3.9, and the range now highlights the specific sub-expression that is invalid rather than highlighting the whole decorator ## Test Plan Inline snapshots are updated, and new ones are added.
Summary
This PR detects the relaxed grammar for decorators proposed in PEP 614 on Python 3.8 and lower.
The 3.8 grammar for decorators is here:
in contrast to the current grammar here
Test Plan
New inline parser tests.