Skip to content
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

loader: attempt to fix compatibility with importlib's LazyLoader #7657

Merged
merged 4 commits into from May 25, 2023

Conversation

rokm
Copy link
Member

@rokm rokm commented May 24, 2023

When a module is lazily imported using importlib.util.LazyLoader, the module's spec is modified and the loader_state member is overwritten with a dictionary constructed by the LazyLoader's code. Unfortunately, this breaks our frozen importer, because our find_spec implementation uses that field to store the PYZ entry name, and exec_module reads it before trying to look-up the code object in the PYZ. (And this seems to be necessary to handle aliased modules, such as pkg_resources._vendor.jaraco.text being aliased as pkg_resources.extern.jaraco.text).

See for an example in the wild: #7655 (comment)

Add the prefix to FrozenImporter to distinguish it from built-in
FrozenImporter(s), such as the one from `_importlib_frozen˙.
@rokm rokm force-pushed the frozen-importer-and-lazy-loader branch 4 times, most recently from f53b7cb to a903304 Compare May 24, 2023 14:29
rokm added 3 commits May 24, 2023 16:50
Use official importlib.util.LazyLoader example to create tests
with lazy import of:
 - a stdlib module
 - a 3rd party module with alias, using original name
 - a 3rd party module with alias, using the alias
Our `PyiFrozenImporter.find_spec` implementation uses
`ModuleSpec.loader_state` to store the original entry name under
which the module can be found within the PYZ archive. The
`PyiFrozenImporter.exec_module` then uses this information to
retrieve the module's code object from the PYZ archive, even if
the module's name (in `ModuleSpec.name`) is changed.

However, it seems that when using `importlib.util.LazyLoader`,
`PyiFrozenImporer.exec_module` gets called with module object
whose spec contains modified `loader_state`.

Therefore, have our `find_spec` use a custom object type for
`loader_state`; this allows `exec_module` to always determine
whether the `loader_state` came from our `find_spec` or not.
For now, in the latter case we raise an error.
When `importlib.util.LazyLoader` is used, the `module` passed to
the `PyiFrozenImporter.exec_module` contains modified spec, in
which the `loader_state` is a `dict` prepared by the `LazyLoader`
code.

This means that our original `loader_state` is lost, and we need
to determine the module name for look-up in the PYZ by other means.
Specifically, we convert the `origin` back to the module name, which
allows us to handle aliased modules as well.
@rokm rokm force-pushed the frozen-importer-and-lazy-loader branch from a903304 to 8ed13ab Compare May 24, 2023 14:50
@rokm rokm merged commit b5f55cf into pyinstaller:develop May 25, 2023
18 checks passed
@rokm rokm deleted the frozen-importer-and-lazy-loader branch May 25, 2023 08:41
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jul 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants