Skip to content

py/builtinimport: Look in every directory of PATH. #10814

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 3 commits into
base: master
Choose a base branch
from

Conversation

smurfix
Copy link
Contributor

@smurfix smurfix commented Feb 22, 2023

Scan all elements of "usys.path" for (sub)modules.

Previously, this scan was only done for toplevel modules, which prevented selectively overriding a submodule. This was a problem in low-memory situations, particularly when debugging.

Closes #10813.

NB, while this is somewhat less efficient than the previous code, the overhead should be negligible compared to reading the file and possibly byte-compiling it. The speed loss is also offset by the fact that you can now selectively override single submodules instead of copying the whole module tree to the device (assuming that this is possible in the first place).

We now scan all elements of "usys.path" for (sub)modules.

Previously, this scan was only done for toplevel modules,
which prevented selectively overriding a submodule.
This was a problem in low-memory situations.
@github-actions
Copy link

Code size report:

   bare-arm:    +0 +0.000% 
minimal x86:   -96 -0.052% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
        rp2:    +8 +0.002% PICO

@jimmo
Copy link
Member

jimmo commented Feb 23, 2023

Thanks @smurfix -- I investigated a different approach to a similar problem in #8922

I'm curious how this comes up in the non-frozen case? i.e. how do you end up with two on-filesystem hierarchies.

That said, I'm not sure I realised that MicroPython's behavior here is actually different to CPython, and if I understand correctly, the approach used in this PR actually improves CPython compatibility? (And would solve the frozen scenario I outlined in my PR).

@jimmo
Copy link
Member

jimmo commented Feb 23, 2023

Sorry I didn't see you'd already mentioned this already in #10813.

I agree with what you said there, but there needs to be some thought as to maintaining CPython compatibility.

In particular consider this case (which I think is likely a common case for what you're trying to improve):

.frozen:
    foo
        __init__.py   `from .a import x; from .b import y`
        a.py    `def x(): print(".frozen x()")`
        b.py  `def y(): print(".frozen y()")`

user does "import foo; foo.x"

Now the user creates on the filesystem

dev
    foo
        a.py  `def x(): print("dev x()")`

(i.e. they've patched a.py for development, but left the rest of the foo module unchanged. this could be uasyncio/core.py for example)

Then

import sys
sys.path.insert(0, "dev")  # make dev searched before .frozen

import foo
foo.x()

On CPython, this will ignore the "dev" directory entirely. On current MicroPython, and this PR, it will ignore frozen/foo/__init__.py (i.e. import foo gets the empty module from dev/foo).

We could make it a requirement that you must also override all __init__.py, i.e. you now add dev/foo/__init__.py identical to frozen/foo/__init__.py but this will now fail on the from .b import y.

(In summary, I think this PR works well as long as you're entirely working with namespace packages, but as soon as __init__.py gets involved, it's very confusing. MicroPython currently essentially only supports non-namespace packages. Realistically, I think many of the scenarios where you want this ability to override frozen code are for non-namespace packages).

@smurfix
Copy link
Contributor Author

smurfix commented Feb 23, 2023

IMHO whether CPython does or does not ignore the "dev" directory, and/or whatever subhierarchy still is accessible because it assumes your packages are namespace-y, is mostly immaterial because on CPython you simply create a copy of the whole "foo" subtree when you need to edit it, point your PYTHONPATH to it, and you're set.

We can't do that on MicroPython because doing so requires too much RAM.

this PR works well as long as you're entirely working with namespace packages

Well, this patch essentially namespace-ifies all packages, and thus behaves exactly the same as CPython – except for the fact that it doesn't require __init__.py to either not exist anywhere, or to contain the magic __path__ = __import__('pkgutil').extend_path(__path__, __name__) compatibility stanza (which BTW you could use to get your dev example to work on CPython).

I don't think there's much confusion with __init__.[m]py. Conceptually you simply put the directories on top of each other and the first file that's found in any of them gets used, with the single proviso that foo/__init__.[m]py always overrides foo.[m]py – which in practice would be (a) rare, and (b) exactly what you want (supersede a small in-ROM package with a larger one in the Flash filesystem).

Realistically, I think many of the scenarios where you want this ability to override frozen code are for non-namespace packages

Given that currently there aren't any namespace-y packages on MicroPython, you're correct by definition. however, the chance that somebody has a foo/bar/baz.py lying around on their filesystem which (a) is in their usys.path before .frozen and which they don't want to override the version from ROM are small enough that documenting this change of behavior in the release notes should be way more than sufficient to ward off any problems.

@peterhinch
Copy link
Contributor

I can't lay claim to a solution, but here are a few thoughts.

  1. By definition the problem is one for experienced developers who can compile a build.
  2. Therefore could we consider a build option? In this case the option might override standard Python behaviour.
  3. Given that we are talking about large projects it's likely that .mpy files will be required. In the developer tree a .mpy should be preferred over a .py.
  4. Any solution needs to consider an __init__.py that uses Damien's lazy loader (as per uasyncio).

@dpgeorge dpgeorge added the py-core Relates to py/ directory in source label Feb 24, 2023
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.

Look for modules in every directory of usys.path
4 participants