I need to import .py
source files from my package into alternative locations in the module hierarchy. (See Custom importer for overriding a buggy vendored transitive dependency for why I need to do that.) It is essential that the __name__
of each module is accurate to the target location at load time, i.e.
from <mypackage> import patched_six
sys.modules["six"] = patched_six
does not work.
I have an approach that works using importlib.util.spec_from_file_location
, but it needs importlib.resources.as_file
:
from importlib import resources, util
def load_patched_module(mod_path, patch_name):
"""
Load the Python source file {patch_name}, which is a sibling of
this file, as if it were the module named {mod_path}.
"""
src = resources.files(__package__).joinpath(patch_name + ".py")
with resources.as_file(src) as src_path:
spec = util.spec_from_file_location(mod_path, src_path)
mod = util.module_from_spec(spec)
sys.modules[mod_path] = mod
spec.loader.exec_module(mod)
It seems to me that I ought to be able to avoid using as_file
here, but I cannot figure out how to acquire the loader that would be used for {__package__}.{patch_name}
and then make it load that file as-if it were named {mod_path}
. I tried
spec = util.find_spec(f"{__package__}.{patch_name}")
spec.name = mod_path
mod = util.module_from_spec(spec)
sys.modules[mod_path] = mod
spec.loader.exec_module(mod)
but this throws an ImportError on the exec_module
step:
ImportError: loader for {__package__}.{patch_name} cannot handle {mod_path}
and this is where I got stuck.
(p.s. I also don’t understand why
spec = util.find_spec(f"{__package__}.{patch_name}")
returns a valid module spec but
spec = util.find_spec(patch_name, __package__)
returns None.)