-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
[WIP] Adds positional only args support (PEP 570) #2927
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
Conversation
|
Thanks, I'll look through it soon.
There's a compiler directive that can disable this behaviour, but yes, it's done by default for performance reasons. |
You can declare some or all of the errors as non-fatal to keep the parser going by passing |
scoder
left a comment
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.
Very nice, sorry for the heap of comments.
tests/run/posonly.pyx
Outdated
| """ | ||
| return a + b + c | ||
|
|
||
| # TODO: remove the test below? would need to hard-code the function with > 255 posonly args |
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.
OT1H, why not? OTOH, this tests for a specific limitation that CPython used to have at some point and that was lifted. Cython never had it.
Still, I think it would be nice to have a test for a function with a large set of arguments. Just generate a long signature line with arguments a1 through a300, text-wrap it, and paste it 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.
Done
tests/run/posonly.pyx
Outdated
|
|
||
| import cython | ||
|
|
||
| # TODO: add the test below to an 'error' test |
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.
You can create an error test in the tests/errors/directory (and also use # mode: error for it). See the examples there.
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.
Done, but with some tests commented out (see my overall comment above)
tests/compile/posonly.pyx
Outdated
| # tag: posonly | ||
|
|
||
| # TODO: remove posonly tag before merge (and maybe remove this test, | ||
| # since it seems covered by the runs/ test) |
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.
Yes, the runnable tests seem enough. You can always use the run tests as translate-only tests by passing --cython-only to the test runner.
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've removed the compile only tests
tests/run/posonly.pyx
Outdated
| pass | ||
|
|
||
| #def test_change_default_pos_only(): | ||
| # TODO: probably remove this, since we have no __defaults__ in Cython? |
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.
Yes, we do, if you set the compiler directive binding=True (which would be ok for this test).
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.
Good to know, done. Some of these are tests are still commented out for two reasons:
(1) In the CPython tests, __defaults__ is modified during the test and this affects later calls to the function. Is there a way to enable this feature (writability of __defaults__) in Cython?
(2) The tests refer to the member f.__code__.co_posonlyargcount. This is a new addition to PyCodeObject as part of the posonly change. Since the posonly change has yet to be merged into CPython, this member is missing currently.
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 writing to
__defaults__is a separate feature. Not worth testing here. - Sounds like a
hasattr()might help. We are testing a CPython feature here, after all, not really a Cython feature. It's more like "if CPython supports it, then Cython should do the right thing. If not, then that's fine, too".
tests/run/posonly.pyx
Outdated
| import cython | ||
|
|
||
| # TODO: add the test below to an 'error' test | ||
| #def test_invalid_syntax_errors(): |
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.
Interesting. I don't see an error test that uses more than one slash in the signature. That seems worth testing.
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.
Looks like such tests have been added to the CPython implementation tests too, and I've copied them here. They are all commented out currently though.
tests/run/posonly.pyx
Outdated
| Traceback (most recent call last): | ||
| TypeError: test_use_positional_as_keyword1() takes no keyword arguments | ||
| """ | ||
| pass |
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.
You don't actually need the pass 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.
Noted, I've removed this in many places.
tests/run/posonly.pyx
Outdated
| # lambda *, a, /: None | ||
| # lambda *, /, a: None | ||
|
|
||
| class Example: |
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.
class Example(object):, same for class X(object) below.
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.
Done
tests/run/posonly.pyx
Outdated
| def f(self, a, b, /): | ||
| return a, b | ||
|
|
||
| def test_posonly_methods(): |
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.
You can also test this in the docstring of the class, no need for a separate test function 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.
Done
tests/run/posonly.pyx
Outdated
| def global_pos_only_f(a, b, /): | ||
| pass | ||
|
|
||
| def test_module_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.
Just use the docstring of the function itself, no need for a separate test 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.
Done
|
Thanks for the feedback! I've pushed a commit which addresses your comments. There is also a change to the main logic itself to fix an indexing issue resulting from the change to The remaining issues: (2) One of the tests refers to (3) Lastly I want to carefully check all possible code-generation paths for argument unpacking are flexed by the tests. |
0b0042b to
206565e
Compare
(Author of PEP570 here): I have merged the implementation of PEP570 in upstream Python today, so this should work against the latest master. Thank you very much for working on this and ping me if you need anything :) |
|
@rjtobin, regarding 1), I think we can afford having some additional error test files for special cases. Just give them a common name prefix and some |
scoder
left a comment
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.
Very nice! I think we can fix the C-API issue separately.
| """ | ||
| return a + b + c | ||
|
|
||
| # TODO: this causes a line that is too long for old versions of Clang |
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.
Interesting. See? It was worth testing. :)
|
Very nice improvement. I added one little optimisation, which is to stick to a Another thing I noticed: The positional argument parsing code looks less efficient now. For example, this signature: generates this code: First of all, no keywords are allowed here, so passing them can raise an error straight away. Secondly, note how the Would you want to write up a separate PR that optimises this? |
|
Sure, I’m happy to make another PR. Will work on it during the week. Thanks for the all the feedback! |
This is supposed to address #2915 (though it covers Python functions only). I'm new to the Cython codebase, so any guidance is appreciated. I've marked it as WIP since the tests need a little more work (see below). The core code is supposed to be functional though, and passes the current set of tests.
The tests are adapted from those in the reference CPython implementation. I wasn't sure how to express some of these tests in Cython's framework (eg.
test_invalid_syntax_lambda): certain syntax errors cause the parser to exit immediately (eg.lambda *args, /: None), and so subsequent errors don't get tested. I know I could make many different test files, but I don't want to clutter up the tests directory with many files for one new feature. Is there a better way to implement these tests? These tests have been commented out, along with some others that don't seem applicable to Cython.I noticed when implementing this that for single-argument methods with no default value, Cython already compiles these to functions that do not allow keyword arguments. Eg.
I'm guessing since this allows for an optimization that it is not considered a bug? (ie. the resulting method can be
METH_Oinstead ofMETH_VARARGS | METH_KEYWORDSand needs no argument parsing).Thanks for maintaining such a great project!