Skip to content

bpo-43897: Implement AST validation for Pattern Matching #24771

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

Merged
merged 1 commit into from
Jul 28, 2021

Conversation

isidentical
Copy link
Member

@isidentical isidentical commented Mar 6, 2021

@github-actions
Copy link

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

@github-actions github-actions bot added the stale Stale PR or inactive for long period of time. label Apr 11, 2021
@isidentical isidentical changed the title bpo-42128: Implement AST validation for Pattern Matching bpo-43897: Implement AST validation for Pattern Matching Apr 20, 2021
Copy link
Member

@brandtbucher brandtbucher left a comment

Choose a reason for hiding this comment

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

Hi @isidentical!

I think I missed this in the beta-freeze scramble. Any plans to update it for the new AST?

@isidentical
Copy link
Member Author

I think I missed this in the beta-freeze scramble. Any plans to update it for the new AST?

I might take a look at it in the upcoming weeks though I don't think I would have enough capacity to catch up with beta 2 (9 days from now). If you have time, I could give access to my fork if not I'll try to make it ready for beta 3.

@brandtbucher
Copy link
Member

Beta 3 is fine!

@isidentical isidentical force-pushed the pattern-matching-validator branch from 0356aea to d758c6a Compare June 12, 2021 17:43
@isidentical
Copy link
Member Author

Hey @brandtbucher! I've updated the PR with the new AST form, sorry it took a bit long. I didn't have much free time after the AST layout change, and needed to catch up with the new nodes first. Just ported all the missing pieces, there are still some bugs that I can see (for example some of the old parts of the validator code just quit when error happens, but doesn't decrement the recursion limit. Since I didn't touch those and the patch was already big I just skipped them now, might fix later) but nothing too big. Please let me know any missing stuff that I should handle.

@isidentical isidentical requested a review from brandtbucher June 12, 2021 17:46
@isidentical isidentical added 🔨 test-with-buildbots Test PR w/ buildbots; report in status section and removed stale Stale PR or inactive for long period of time. labels Jun 12, 2021
@bedevere-bot
Copy link

🤖 New build scheduled with the buildbot fleet by @isidentical for commit d758c6a1e9f6c01800822c5bdf5e1778161fab3f 🤖

If you want to schedule another build, you need to add the ":hammer: test-with-buildbots" label again.

@bedevere-bot bedevere-bot removed the 🔨 test-with-buildbots Test PR w/ buildbots; report in status section label Jun 12, 2021
@isidentical isidentical requested a review from pablogsal June 28, 2021 07:33
Copy link
Member

@brandtbucher brandtbucher left a comment

Choose a reason for hiding this comment

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

Thanks, this looks really good! I just have a few comments:

cases = [
ast.match_case(
pattern=pattern,
body = [ast.Expr(ast.Constant(...))]
Copy link
Member

Choose a reason for hiding this comment

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

Took me a sec to see what was happening here. Maybe clearer as ast.Pass()?

Suggested change
body = [ast.Expr(ast.Constant(...))]
body = [ast.Pass()]

Comment on lines +1576 to +1580
ast.MatchMapping(
[constant_true, ast.Starred(ast.Name('lol', ast.Load()), ast.Load())],
[pattern_x, pattern_1],
rest='legit'
),
Copy link
Member

Choose a reason for hiding this comment

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

This looks the exact same as the pattern above, right?

Suggested change
ast.MatchMapping(
[constant_true, ast.Starred(ast.Name('lol', ast.Load()), ast.Load())],
[pattern_x, pattern_1],
rest='legit'
),

Python/ast.c Outdated
Comment on lines 451 to 467
case Constant_kind: {
/* Ellipsis and immutable sequences are not allowed.
For True, False and None, MatchSingleton() should
be used */
if (!validate_expr(state, exp, Load)) {
return 0;
}
PyObject *literal = exp->v.Constant.value;
if (PyLong_CheckExact(literal) || PyFloat_CheckExact(literal) ||
PyBytes_CheckExact(literal) || PyComplex_CheckExact(literal) ||
PyUnicode_CheckExact(literal)) {
return 1;
}
PyErr_SetString(PyExc_ValueError,
"unexpected constant inside of a literal pattern");
return 0;
}
Copy link
Member

Choose a reason for hiding this comment

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

I don't think these braces are needed, right?

Suggested change
case Constant_kind: {
/* Ellipsis and immutable sequences are not allowed.
For True, False and None, MatchSingleton() should
be used */
if (!validate_expr(state, exp, Load)) {
return 0;
}
PyObject *literal = exp->v.Constant.value;
if (PyLong_CheckExact(literal) || PyFloat_CheckExact(literal) ||
PyBytes_CheckExact(literal) || PyComplex_CheckExact(literal) ||
PyUnicode_CheckExact(literal)) {
return 1;
}
PyErr_SetString(PyExc_ValueError,
"unexpected constant inside of a literal pattern");
return 0;
}
case Constant_kind:
/* Ellipsis and immutable sequences are not allowed.
For True, False and None, MatchSingleton() should
be used */
if (!validate_expr(state, exp, Load)) {
return 0;
}
PyObject *literal = exp->v.Constant.value;
if (PyLong_CheckExact(literal) || PyFloat_CheckExact(literal) ||
PyBytes_CheckExact(literal) || PyComplex_CheckExact(literal) ||
PyUnicode_CheckExact(literal)) {
return 1;
}
PyErr_SetString(PyExc_ValueError,
"unexpected constant inside of a literal pattern");
return 0;

Python/ast.c Outdated
break;
case MatchMapping_kind:
// TODO: check "rest" target name is valid
case MatchMapping_kind: {
if (asdl_seq_LEN(p->v.MatchMapping.keys) != asdl_seq_LEN(p->v.MatchMapping.patterns)) {
PyErr_SetString(PyExc_ValueError,
"MatchMapping doesn't have the same number of keys as patterns");
return 0;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return 0;
ret = 0;
break;

Python/ast.c Outdated
break;
case MatchMapping_kind:
// TODO: check "rest" target name is valid
case MatchMapping_kind: {
Copy link
Member

Choose a reason for hiding this comment

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

These braces aren't needed either, right?

Suggested change
case MatchMapping_kind: {
case MatchMapping_kind:

Python/ast.c Outdated
break;
}
Copy link
Member

Choose a reason for hiding this comment

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

See above.

Suggested change
}

Python/ast.c Outdated
Comment on lines 569 to 572
} else if (cls->kind == Attribute_kind) {
cls = cls->v.Attribute.value;
continue;
} else {
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
} else if (cls->kind == Attribute_kind) {
cls = cls->v.Attribute.value;
continue;
} else {
}
else if (cls->kind == Attribute_kind) {
cls = cls->v.Attribute.value;
continue;
}
else {

Python/ast.c Outdated
break;
}
case MatchClass_kind:
if (asdl_seq_LEN(p->v.MatchClass.kwd_attrs) != asdl_seq_LEN(p->v.MatchClass.kwd_patterns)) {
PyErr_SetString(PyExc_ValueError,
"MatchClass doesn't have the same number of keyword attributes as patterns");
return 0;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return 0;
ret = 0;
break;

Python/ast.c Outdated
case MatchClass_kind:
if (asdl_seq_LEN(p->v.MatchClass.kwd_attrs) != asdl_seq_LEN(p->v.MatchClass.kwd_patterns)) {
PyErr_SetString(PyExc_ValueError,
"MatchClass doesn't have the same number of keyword attributes as patterns");
return 0;
}
// TODO: Restrict cls lookup to being a name or attribute
if (!validate_expr(state, p->v.MatchClass.cls, Load)) {
return 0;
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
return 0;
ret = 0;
break;

@bedevere-bot
Copy link

When you're done making the requested changes, leave the comment: I have made the requested changes; please review again.

@brandtbucher
Copy link
Member

Just ported all the missing pieces, there are still some bugs that I can see (for example some of the old parts of the validator code just quit when error happens, but doesn't decrement the recursion limit. Since I didn't touch those and the patch was already big I just skipped them now, might fix later) but nothing too big.

Sorry, didn't see this. Feel free to ignore those suggestions, then!

I can find time to go back over those and fix the "_" name validation issue sometime if you're too busy. Just let me know.

@isidentical
Copy link
Member Author

I can find time to go back over those and fix the "_" name validation issue sometime if you're too busy. Just let me know.

What do you mean by the _ validation (example case would be welcome)? By the way feel free to apply style suggestions by yourself since it is quite hard to deal with that sort of diffs in GitHub interface :(

@brandtbucher
Copy link
Member

What do you mean by the _ validation (example case would be welcome)?

Patterns like MatchMapping(..., rest="_"), MatchStar("_"), and MatchAs(..., name="_") should be rejected, but aren't.

Also, MatchStar is allowed as a top-level pattern, but shouldn't be. I'll just merge this and fix those, the remaining recursion depth bugs, and remove some outdated comments in a new PR today (it will be easier to backport to 3.10 that way).

@brandtbucher brandtbucher merged commit 31bec6f into python:main Jul 28, 2021
@brandtbucher
Copy link
Member

Thanks again! Sorry I kept dropping the ball on this, haha.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants