Skip to content

pep 570 positional only parameters. #8480

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

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

jbbjarnason
Copy link
Contributor

@jbbjarnason jbbjarnason commented Apr 2, 2022

In CPython 3.8 the proposed positional only parameter indicator (slash) was introduced.

The proposal: https://peps.python.org/pep-0570/
The python bug report tracking the pep 570 development: https://bugs.python.org/issue36540

In short the pep 570 introduces a slash in the function definition where all parameters to the left of the slash are considered positional only. Therefore, can not be called with keyword.

Example:

def name(p1, p2, /, p_or_kw, *, kw):

Tests were duplicated from the introduction to this syntax from CPython with slight modifications.

This was worked on with the assistance of @norandomtechie

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Apr 4, 2022
@dpgeorge
Copy link
Member

dpgeorge commented Apr 4, 2022

Thanks for the contribution. I think this is an important addition for MicroPython because it allows a nice optimisation, where the names of parameters don't need to be stored alongside the function definition. But I don't think that's done here? In particular this part of the code in py/bc.c:

            const uint8_t *arg_names = code_state->ip;
            arg_names = mp_decode_uint_skip(arg_names);

@jbbjarnason
Copy link
Contributor Author

Thanks for the contribution. I think this is an important addition for MicroPython because it allows a nice optimisation, where the names of parameters don't need to be stored alongside the function definition. But I don't think that's done here? In particular this part of the code in py/bc.c:

            const uint8_t *arg_names = code_state->ip;
            arg_names = mp_decode_uint_skip(arg_names);

You are correct, we did not think of making this optimization. We will add it. Changing to draft while we make the changes.

@jbbjarnason jbbjarnason marked this pull request as draft April 4, 2022 17:22
@jbbjarnason jbbjarnason force-pushed the pep/570/positional-only-arguments branch 4 times, most recently from 692494a to 9adf2e4 Compare April 6, 2022 21:41
jbbjarnason and others added 2 commits April 9, 2022 11:26
py/bc: Add pos-only to encode/decode bytecode.
py/compile: Raise exception when slash in invalid position.
py/grammar: Allow slash in argslist_item.
py/objfun: Change according to py/bc.
py/persistentcode: Add pos-only to bytecode_prelude.
py/profile: Add pos-only to prelude extract.
py/scope: Add pos-only to scope_t.
tools/mpy-tool: Reflect on changes to prelude.
Ported over CPython tests to test positional-only args.
Expected output of the run given.
@jbbjarnason jbbjarnason force-pushed the pep/570/positional-only-arguments branch from 9adf2e4 to 68cb619 Compare April 9, 2022 11:31
@jbbjarnason jbbjarnason force-pushed the pep/570/positional-only-arguments branch from 68cb619 to 3efbea4 Compare April 9, 2022 11:35
@jbbjarnason jbbjarnason marked this pull request as ready for review April 9, 2022 11:53
@jbbjarnason
Copy link
Contributor Author

@dpgeorge marked as ready for review, I changed the appropriate emit functions (bc&native) to skip storing arg_names for positional only parameters.

@dpgeorge
Copy link
Member

Regarding the prelude signature encoding, xSSSSEAA [xFSSKAED repeated]: a lot of analysis went into this to determine a good encoding scheme. Basically the entire CPython standard library was analysed to see what was most common and the encoding designed to be as small as possible (while still being not too complex).

Did you consider any other way to store the new number needed by this PR (number of positional-only args)?

One way to test the impact of the change here is to compile (using mpy-cross) a whole lot of .py files before and after the change and compare the total size of all resulting .mpy files.

Removing the pos only parameter count
from the pos_args counter.
@jbbjarnason
Copy link
Contributor Author

Regarding the prelude signature encoding, xSSSSEAA [xFSSKAED repeated]: a lot of analysis went into this to determine a good encoding scheme. Basically the entire CPython standard library was analysed to see what was most common and the encoding designed to be as small as possible (while still being not too complex).

Yes I guessed so while I read through the git history of the bc.h.

Did you consider any other way to store the new number needed by this PR (number of positional-only args)?

We began implementing this with this pattern: xSSSSEAA [xFSPKAED repeated] where P=pos only but I was concerned it was too much of a change to remove one bit from the code state. So back to the drawing board.
My guess was that on average K would be the smallest number of FAKD. Given that K is the smallest number on average we injected P into its place with modulo of 2 placement (my guess is that this method is optimal).

If your question is if we considered placing it elsewhere in the byte/native code than the answer is no. Not sure where to look.

One way to test the impact of the change here is to compile (using mpy-cross) a whole lot of .py files before and after the change and compare the total size of all resulting .mpy files.

I am not sure if the best approach is to do an before and after analysis, generating mpy files with the code before restricts the usage of pos only parameters and we would probably see increased size. I am however unsure what the best approach is, maybe this is the only way.

At last, I added a new commit which could make the byte/native code smaller when positional parameters are used. I have yet to squash it into the implementation commit.

@dpgeorge
Copy link
Member

dpgeorge commented Jun 3, 2022

We began implementing this with this pattern: xSSSSEAA [xFSPKAED repeated] where P=pos only but I was concerned it was too much of a change to remove one bit from the code state. So back to the drawing board.

I think this scheme is actually a good idea (although maybe xFPSKAED, it makes the decoding of S slightly more efficient).

I ran some tests and removing 1 bit from S in the trailing bytes makes very little difference to encoding size. Compiling all code in tests/basics/*.py, the total size of all .mpy's was 4 bytes bigger removing a bit from S (173470 bytes total to 173474 bytes total). Admittedly the tests are rather small functions. Testing uasyncio and aioble, they had not change in the size of their .mpy files (total 8464 and 18079 bytes respectively).

I did some other tests with this PR, modifying some of uasyncio to use the new / feature. Then compiling stm32 firmware. The size of frozen .mpy files does decrease if only 1 bit of S is in the trailing byte (so P and K have their own bits).

@dpgeorge
Copy link
Member

dpgeorge commented Jun 3, 2022

Some other points:

  1. Freezing code that uses this new / feature doesn't work, mpy-tool.py raises an exception (I think it's an easy fix though).
  2. It's very good to have tests as part of the PR, but the way they are written is very much out of style with the existing test suite. Tests should just print out results of expressions, and then that's checked against the output from running the test under CPython. Use of assert in tests should be mostly avoided.

@jbbjarnason
Copy link
Contributor Author

Okay, so my plan is to:

  1. Revert to xFPSKAED
  2. Figure out what is wrong with mpy-tool.py
  3. Format tests according to your comments
    I will most likely do this in August.

@dpgeorge
Copy link
Member

Now that v1.19 is released with mpy format v6, I don't want to change the bytecode format for at least a couple of releases. So it would be good to split this PR into two separate pieces:

  1. implement the syntax so that code will be accepted, and anything else that can be done without changing the bytecode format
  2. implement the remaining changes, ie xFPSKAED encoding

@jepler jepler mentioned this pull request Jun 16, 2023
35 tasks
tannewt pushed a commit to tannewt/circuitpython that referenced this pull request Oct 20, 2023
…lizzard_s3

Added Unexpected Maker BlizzardS3 board for Espressif port.
@smurfix
Copy link
Contributor

smurfix commented Apr 10, 2025

Any progress on this?

@jbbjarnason
Copy link
Contributor Author

jbbjarnason commented Apr 10, 2025

@smurfix Sorry but I havent spent any time with this. I would suggest forking the branch and rebase to continue with this, and open a PR from that new fork/branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
py-core Relates to py/ directory in source
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants