-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
py: Add PEP 750 template strings support #17557
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
base: master
Are you sure you want to change the base?
py: Add PEP 750 template strings support #17557
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #17557 +/- ##
==========================================
+ Coverage 98.38% 98.43% +0.05%
==========================================
Files 171 174 +3
Lines 22297 23035 +738
==========================================
+ Hits 21936 22674 +738
Misses 361 361 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
Code size report: |
dpgeorge
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.
Thanks for the contribution! This will be nice to have for the webassembly port.
I didn't do a review yet, but there will need to be tests to get full coverage of the new code.
|
@dpgeorge Thank you for the feedback and for taking time to look at this! I'm glad this will be useful for the webassembly port. I'll add more tests to ensure full coverage when I find time. |
|
@koxudaxi slightly off topic - but I notice you'll be at EuroPython in Prague, as will I. We should look out for each other and have a coffee or lunch together! 🇪🇺 🐍 |
|
@ntoll Sounds good! See you in Prague. ☕ |
7a1dc11 to
50dd4d1
Compare
I tried really hard to make 100% coverage but some code is never called and I cannot cover it. Do you know how to fix this? |
|
@dpgeorge |
| return MP_OBJ_FROM_PTR(result); | ||
| } | ||
|
|
||
| case MP_BINARY_OP_REVERSE_ADD: { |
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 just remove this case, it's not needed.
dcddb2a to
e648074
Compare
|
Feel free to ping me once you've finished working through this to a state that's ready to be re-reviewed. |
3d82482 to
8d12ba3
Compare
|
@kumekay are you paying attention to hints? if you need any help at all, mind stating what is it? I am following pushes and I feel like you are stuck in some testing loop and there's zero shame in that ... but maybe others can help with? Surely not helping your case but I see tons of pushes daily and I kinda sympathize with your possible frustration, specially when I see tests added or removed to make CI green .... maybe there's a better way, as everything else feels right in place so that asking "how do I make CI happy in there?" might be a faster way out. |
@WebReflection |
|
@WebReflection I think I've squashed most of the bugs, and what remains is the module path issue. @AJMansfield @dpgeorge https://docs.python.org/3/library/string.templatelib.html I believe everything else should be fine, so I'd appreciate another review when you have a chance. |
Normally, implementing a module as frozen mpy bytecode will actually be smaller than the equivalent implementation in C. It's also a common pattern to define an underscore-prefixed module in C to provide a minimal set of primitives to access some interpreter feature or api or perform the performance-critical hot loop of a package, and then implement the rest of that module in python. For an example of this, take a look at how the Instead of defining the recursive module structure manually in Also, given the massive code size increase at stake here, it might even be worth considering if there's a way to move even more of the parse logic into something that can run from bytecode --- perhaps even, for some ports, as an externally mip-installable component rather than something to freeze in. For instance, have MicroPython's parser just minimally recognize static mp_obj_t tstring_parse_func = NULL;
mp_obj_t tstring_parse(vstr_t *str) {
if (tstring_parse == NULL) {
mp_obj_t modtstring = mp_builtin___import__(1, &MP_OBJ_NEW_QSTR(MP_QSTR_mp_tstring_support));
// NLR this to a "tstring support not installed" error? Maybe with directions to run `mip.install("mp_tstring_support")`?
tstring_parse_func = mp_obj_dict_get(mp_obj_module_get_globals(modtstring), MP_QSTR_parse);
}
return mp_call_function_1(tstring_parse_func, mp_obj_new_str_from_vstr(str));
} |
|
@AJMansfield (If I understand correctly, no changes to |
Will do! Hopefully I'll be able to get to this on Monday.
That's my assumption as well --- I wouldn't put too much stock in my musings on that idea, for whether or not it might be possible to let portions of the parser operate from bytecode. If that's anything at all, it's certainly not something I'd put in the way of the current version being merged. |
f99c872 to
9d2d54a
Compare
d4b5608 to
d8c0a60
Compare
|
@AJMansfield |
|
Apologies for the delay reviewing, finally found the time though. And that's a well-organized set of commits I'll have no trouble examining. |
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.
On examination of the code and after running it on my machine, I can say that the functional code in this PR seems substantively correct, but there's a few details to still clean up especially with regards to testing.
Most of the tests here do not pass when run in comparison with a real CPython 3.14 instance (i.e. --- but with only a small number of lines differing, on error behavior and other areas where differences are commonly accepted. Usually, when testing a feature that's broadly compatible but with a minor deviation, it's a common pattern to separate out the compatible and incompatible cases between e.g. some_test.py and some_test1.py, so that you can provide a some_test1.py.exp without removing the cross-compare for all of it.
It's reasonable enough to also be using .exp files here to bridge the gap to 3.14-less testing environments, but I'd like to still see these tests split into separate fully-3.14-compatible and non-3.14-compatible test files. Note that the .exp's for the non-3.14-compatible ones should go in the same commit with the tests --- i.e. it should be a complete commit with everything needed to run and pass a comparison in an environment that does have the right CPython, with the follow-up commit containing only exactly the files generated by running e.g. python3.14 some_test.py &> some_test.py.exp for the relevant tests.
(As a related note, we've actually been needing a mechanism to address this scenario for a while --- ever since CPython changed tracing formats making MicroPython's outputs on the tests for sys.settrace no longer match on newer versions. If you'd be interested in taking on adding that --- maybe a table similar to tests_with_regex_output that gives CPython version ranges to use or disregard exp files on, that would be and then use or disregard their exp file when appropriate --- it would be quite useful indeed to have. If not though, I'll be satisfied with just having the tests split.)
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.
This exp should be in the commit that adds the feature check; it's needed to even run the tstring tests at all.
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.
The feature-check exp file is now part of the same commit as tests/feature_check/tstring.py so the test works immediately
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.
This should probably be a micropython test rather than a basics test, as __template__ is a micropython-specific implementation detail.
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.
The validation test has been moved into tests/micropython so the template coverage is clearly marked as MicroPython-specific.
py/modbuiltins.c
Outdated
| #endif | ||
| { MP_ROM_QSTR(MP_QSTR_ZeroDivisionError), MP_ROM_PTR(&mp_type_ZeroDivisionError) }, | ||
| #if MICROPY_PY_TSTRINGS | ||
| { MP_ROM_QSTR(MP_QSTR___template__), MP_ROM_PTR(&mp_builtin___template___obj) }, |
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.
What's the reasoning for putting this at the end, rather than with the other double-underscore builtins? (Note that __print_repl__ is actually also a micropython-specific builtin.)
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 moved __template__ to group it with the other double-underscore builtins.
90343b7 to
d324425
Compare
|
Regarding the behavioral differences, I recall changing the wording due to resource constraints on 32-bit systems. |
Signed-off-by: Koudai Aono <[email protected]>
Signed-off-by: Koudai Aono <[email protected]>
Signed-off-by: Koudai Aono <[email protected]>
Signed-off-by: Koudai Aono <[email protected]>
Signed-off-by: Koudai Aono <[email protected]>
Signed-off-by: Koudai Aono <[email protected]>
d324425 to
f60f9fe
Compare
Implements PEP 750 template strings for MicroPython.
Started in discussion #17497. Template strings return Template objects instead of strings, allowing access to literal parts and expressions separately.
Changes:
Usage:
Implementation:
Testing:
Tested on unix (coverage, standard, minimal), windows, webassembly.
New tests in tests/basics/:
Feature detection in tests/feature_check/tstring.py - automatic skip when MICROPY_PY_TSTRINGS=0.
All existing tests pass.
Trade-offs:
Code size: ~10 KB when enabled, zero when disabled.
String module: minimal builtin with only templatelib blocks micropython-lib's full string module. Future: coordinate with micropython-lib.
Config: enabled when MICROPY_CONFIG_ROM_LEVEL >= EXTRA_FEATURES, requires MICROPY_PY_FSTRINGS=1.
To disable:
make CFLAGS_EXTRA=-DMICROPY_PY_TSTRINGS=0