This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: load_module() does not link submodule to parent package
Type: behavior Stage:
Components: Versions: Python 3.6, Python 3.4, Python 3.5, Python 2.7
process
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: Nosy List: Arfrever, Pathangi Jatinshravan, brett.cannon, eric.snow, martin.panter, ncoghlan, r.david.murray
Priority: normal Keywords:

Created on 2015-10-11 02:47 by martin.panter, last changed 2022-04-11 14:58 by admin. This issue is now closed.

Messages (9)
msg252753 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-10-11 02:47
The load_module() API adds an entry to sys.modules when loading a submodule, but does not add the submodule as an attribute in the parent package. I am no expert on PEP 302 or the import system, but this feels like a bug to me:

>>> import sys, pkgutil
>>> loader = pkgutil.find_loader("xml.etree")
>>> loader.load_module("xml.etree")
<module 'xml.etree' from '/usr/lib/python3.4/xml/etree/__init__.py'>
>>> sys.modules["xml.etree"]  # Entry added, per documentation and PEP 302
<module 'xml.etree' from '/usr/lib/python3.4/xml/etree/__init__.py'>
>>> hasattr(sys.modules["xml"], "etree")  # But not linked to parent!
False
>>> import xml.etree
>>> xml.etree  # Broken :(
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'module' object has no attribute 'etree'
msg252754 - (view) Author: Pathangi Jatinshravan (Pathangi Jatinshravan) Date: 2015-10-11 03:28
Hi, if this is not too critical of a bug, can I be assigned to this issue?
msg252755 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-10-11 03:36
I'm no expert on pep 302 either, but I'm guessing this is an intentional part of the design.  You'll note that updating the namespace is *not* mentioned as one of the things load_module has to do.  The update of the namespace is something the import system as a whole implements, and I'm not sure where it happens (probably __import__/import_module).

Unless you are saying that this is a regression, it doesn't really matter if it was intentional or not, since if it is not a regression then it's been that way for a long time.
msg252761 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-10-11 04:57
I don’t think it is a regression. But it is annoying, because my code that works fine for top-level modules and packages happens to screw up the import system for deeper-level module names.
msg252810 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2015-10-11 14:58
This is entirely on purpose as you're not meant to call load_module() if you want to import code programmatically; that's what importlib.import_module() is for. The load_module() method -- which is kind of deprecated thanks to exec_module() -- is there purely to initialize the module being imported, with everything else the responsibility of other parts of import. You should only be calling load_module/execx_module tonload a specific set of bytes and to bypass the import machinery entirely, in which case you have to manage any "extras" you want, like setting a submodule on a parent package.
msg252812 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-10-11 15:09
Brett: Martin is calling pkgutil.find_loader, which is not marked as deprecated, and then calling load_module on the returned loader, which apparently does *almost* what he wants but not quite.

Martin: maybe you should explain your use case in more detail.  Probably there is a better way to accomplish it using importlib directly.  Unless one of your issues is needing to write 2/3 compatible code...
msg252814 - (view) Author: Brett Cannon (brett.cannon) * (Python committer) Date: 2015-10-11 15:19
1) I would use https://docs.python.org/3/library/importlib.html#importlib.find_loader instead of pkgutil

2) there are a bunch of ways to get a hold of a loader and its hard to document how to use a loader properly since it's low level like __path__ while being crucial for stuff like reloading and loaders predate __spec__
msg252975 - (view) Author: Martin Panter (martin.panter) * (Python committer) Date: 2015-10-14 05:59
My use case is for Readline auto-completion, to list submodules of a package given by the user, without importing anything unnecessary. The original code is compatible with Python 2, but I am also writing a patch for 3.6 that wouldn't need that. The original implementation is like this pseudocode:

def list_submodules(package):
    # Ensure it is a package before loading or importing it
    for name in parent_packages:
        loader = pkgutil.find_loader(name)
        assert loader.is_package()
    # Could call importlib.import_module(), but this seemed easier because I already have the loader:
    package = loader.load_module(name)
    # The only reason I want to load the module:
    search_path = package.__path__
    return pkgutil.iter_modules(search_path)

Thanks for your feedback Brett. I have changed over to importlib.import_module(), and will accept that this is just a quirk of the low level import stuff.
msg252989 - (view) Author: R. David Murray (r.david.murray) * (Python committer) Date: 2015-10-14 12:56
Well, once you've loaded the module and added it to the namespace of the parent package, you've imported it.  Since import_module is available in 2.7, it sounds like that satisfies your use case.
History
Date User Action Args
2022-04-11 14:58:22adminsetgithub: 69559
2015-10-14 12:56:05r.david.murraysetmessages: + msg252989
2015-10-14 05:59:02martin.pantersetmessages: + msg252975
2015-10-11 15:19:30brett.cannonsetmessages: + msg252814
2015-10-11 15:09:00r.david.murraysetmessages: + msg252812
2015-10-11 14:58:10brett.cannonsetstatus: open -> closed
resolution: not a bug
messages: + msg252810
2015-10-11 04:58:45Arfreversetnosy: + Arfrever
2015-10-11 04:57:52martin.pantersetmessages: + msg252761
2015-10-11 04:51:07serhiy.storchakasetnosy: + brett.cannon, ncoghlan, eric.snow
2015-10-11 03:36:49r.david.murraysetnosy: + r.david.murray
messages: + msg252755
2015-10-11 03:28:37Pathangi Jatinshravansetnosy: + Pathangi Jatinshravan
messages: + msg252754
2015-10-11 02:47:12martin.pantercreate