Skip to content

py/builtinimport.c: Allow built-in modules to be packages (v2) #5701

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

Closed
wants to merge 2 commits into from

Conversation

jimmo
Copy link
Member

@jimmo jimmo commented Feb 26, 2020

This is a rework of #4731 based on adafruit#2657

The idea is that a built-in module can add another module as a member of its globals dict (whereas the previous approach was to allow for built-in modules with dots in their name).

I think the main surprise with this approach is that

import foo
foo.bar.baz()

will work, without foo.bar ever being explicitly imported, however there's nothing stopping a regular Python package from doing this either via its __init__.py. That said, if MICROPY_MODULE_BUILTIN_INIT is enabled, then foo.bar's init will not be called in the above example.

The net code size change for this PR is -44 bytes on PYBV11 (but this feature itself, i.e. the final commit, is +56 bytes).

@tannewt @v923z

py/objmodule.c Outdated
#if MICROPY_MODULE_WEAK_LINKS
// Same as mp_module_get, but prefixes
mp_obj_t mp_module_get_builtin_umodule(qstr module_name) {
char buf[MICROPY_ALLOC_PATH_MAX];
Copy link
Member

Choose a reason for hiding this comment

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

To avoid this stack allocation (which can be large) it might be better to pass in a buffer from the caller. Eg the VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX) (not sure if that's allowed to be clobbered tho at the point of calling this function).

Copy link
Member

Choose a reason for hiding this comment

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

Or even pass in the vstr itself and use vstr functions here to construct the u-module name.

Copy link
Member Author

Choose a reason for hiding this comment

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

I reused the path vstr. I'd prefer to do this while the path is empty (i.e. two lines higher) but then you end up creating a new "umodule" QSTR for every import.

py/objmodule.c Outdated

// Returns an already-loaded module, a newly-initialised built-in, or MP_OBJ_NULL otherwise.
mp_obj_t mp_module_get(qstr module_name) {
Copy link
Member

Choose a reason for hiding this comment

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

To decrease code size it might be possible to combine mp_module_get() and mp_module_get_loaded() in to 1 function, with an argument bool search_builtins.

Copy link
Member Author

Choose a reason for hiding this comment

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

Turns out with some more refactoring I don't need both -- can always use the "include builtins" version.

I dug into this much further got the code size down, and made an effort to make the import code a lot easier to follow. Overall PR is now -80 bytes on PYBV11. (i.e. -152 for the first commit, +72 for this feature).

@jimmo jimmo force-pushed the native-packages branch from 2a874dc to f51a9f6 Compare March 5, 2020 04:46
jimmo added 2 commits March 5, 2020 16:13
Clear up the code path for importing:
 - already-loaded modules
 - built-in modules
 - built-in umodules (formerly weak links)
 - filesystem modules
 - (...and in the future, built-in packages)

Also only enables the -m behavior with a (unix-only) flag.

Code size reduction of XX.
Allows a built-in module to add a sub-module as a member of its globals dict, and allow importing
it as "import foo.bar" (or any other variation on the import statement).

Note: This does give the slightly surprising behavior that "import foo" followed by "foo.bar.x"
will also work.

+56 bytes on PYBV11.
@laurensvalk
Copy link
Contributor

Thanks @jimmo - this has been useful.

In case someone else is using this PR as a patch, some findings:

  • It seems to mostly still work if you rebase it onto today's master.
  • A few strings need to be wrapped in MP_ERROR_TEXT due to recent upstream changes.
  • 7 tests failed:
    • uasyncio_event uasyncio_event_fair uasyncio_gather uasyncio_lock uasyncio_lock_cancel uasyncio_micropython uasyncio_wait_for.
    • It seems to fail in getattr(__import__(mod, None, None, True, 1), attr) in extmod/uasyncio/__init__.py
  • This patch is only intended for MICROPY_ENABLE_EXTERNAL_IMPORT enabled. When disabled, you can do something like this.

@jimmo
Copy link
Member Author

jimmo commented Nov 18, 2021

Closing this in favour of #8010 for now (which just simplifies the code but retains the existing behavior). We can revisit the built-in package stuff later.

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Nov 30, 2021
@dpgeorge
Copy link
Member

dpgeorge commented Dec 1, 2021

#8010 was merged in a7fa18c

@dpgeorge dpgeorge closed this Dec 1, 2021
tannewt pushed a commit to tannewt/circuitpython that referenced this pull request Dec 15, 2021
Fetch tools/adabot submodule for website build
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.

3 participants