Repository navigation
Introduce MetaPathFinder.discover and PathEntryFinder.discover #139899
Copy link
Copy link
Closed
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-importlibtype-featureA feature request or enhancementA feature request or enhancement
Description
Proposal:
In certain scenarios, such as implementing error suggestions for ModuleNotFoundError, it would be helpful to have the ability to ask the finder to search for possible specs.
I propose adding a new optional discover method to MetaPathFinder and PathEntryFinder, as follows:
.. method:: discover(parent=None)
An optional method which searches for possible specs with given *parent*.
If *parent* is *None*, :meth:`MetaPathFinder.discover` will search for
top-level modules.
Returns an iterable of possible specs.Python 3.15.0a0 free-threading build (heads/main:744ec1d6c39, Oct 10 2025, 06:22:11) [GCC 15.2.1 20250813] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> path_finder = sys.meta_path[-1]
>>> import sysconfig
>>> for spec in path_finder.discover(sysconfig):
... print(spec)
...
ModuleSpec(name='sysconfig.__init__', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000922c20>, origin='/home/anubis/git/cpython/Lib/sysconfig/__init__.py')
ModuleSpec(name='sysconfig.__main__', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000922ca0>, origin='/home/anubis/git/cpython/Lib/sysconfig/__main__.py')
>>> import urllib
>>> for spec in path_finder.discover(urllib):
... print(spec)
...
ModuleSpec(name='urllib.robotparser', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000922220>, origin='/home/anubis/git/cpython/Lib/urllib/robotparser.py')
ModuleSpec(name='urllib.response', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000920d20>, origin='/home/anubis/git/cpython/Lib/urllib/response.py')
ModuleSpec(name='urllib.__init__', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000921ba0>, origin='/home/anubis/git/cpython/Lib/urllib/__init__.py')
ModuleSpec(name='urllib.request', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000920520>, origin='/home/anubis/git/cpython/Lib/urllib/request.py')
ModuleSpec(name='urllib.parse', loader=<_frozen_importlib_external.SourceFileLoader object at 0x20000921520>, origin='/home/anubis/git/cpython/Lib/urllib/parse.py')
ModuleSpec(name='urllib.error', loader=<_frozen_importlib_external.SourceFileLoader object at 0x200009214a0>, origin='/home/anubis/git/cpython/Lib/urllib/error.py')Possibly, we could also add a helper function that searches sys.meta_path, similarly to importlib.util.find_spec. Although I don't think it is strictly necessary.
def discover(parent=None):
for finder in sys.meta_path:
try:
discover = getattr(finder, 'discover')
except AttributeError:
continue
yield from discover(parent)This allows us to implement suggestions in traceback using the import system abstractions — see https://github.com/FFY00/cpython/tree/ModuleNotFoundError-suggestions.
Python 3.15.0a0 free-threading build (heads/main:744ec1d6c39, Oct 10 2025, 06:22:11) [GCC 15.2.1 20250813] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import asd
Traceback (most recent call last):
File "<python-input-0>", line 1, in <module>
import asd
ModuleNotFoundError: No module named 'asd'. Did you mean: 'ast'?
>>> import importlib.utill
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
import importlib.utill
ModuleNotFoundError: No module named 'importlib.utill'. Did you mean: 'importlib.util'?Has this already been discussed elsewhere?
No response given
Links to previous discussion of this feature:
No response
Linked PRs
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
stdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytopic-importlibtype-featureA feature request or enhancementA feature request or enhancement
Activity
pythongh-139899: Introduce MetaPathFinder.discover and PathEntryFinde…
pythongh-139899: Introduce MetaPathFinder.discover and PathEntryFinde…
Locked-chess-official commentedon Oct 10, 2025
Great. This way is like the method named "__find__" in my first plan. In my full plan, the message "No module named 'importlib.utill'. Did you mean: 'importlib.util'?" changes to "module 'importlib' has no child module 'utill'. Did you mean 'util'?", but it is up to everyone.
importsuggestions forModuleNotFoundError#134872Locked-chess-official commentedon Oct 14, 2025
Now I use my test test_wrong_module_exception for the branch and use the folder structure like it (to avoid for long name):
However, the result is not expect. I will use my tool in friendly_module_not_found_error to find out where is wrong.

Locked-chess-official commentedon Oct 14, 2025
My fault :-( . The suggestion code is not there. I will try for that code.
Locked-chess-official commentedon Oct 14, 2025
OK. Now I sure it is a bug in that branch:
The right now implement on that branch cannot handle on relative import.
Locked-chess-official commentedon Oct 14, 2025
This is the question:
I suggest that the method
discoverand the code when finding the suggestion for ModuleNotFoundError should never import any modules.FFY00 commentedon Oct 14, 2025
Yes, that's a bug on relative imports.
It triggers an import for the parent module, but it should never actually result in a new module being loaded. If the
ModuleNotFoundErroris triggering on the child, it means the parent was successfully imported, so it should always be insys.modulesalready.Locked-chess-official commentedon Oct 14, 2025
This statement is not always true. In relative import, when failed, the module will fail to load so finally it won't appear in
sys.modules.FFY00 commentedon Oct 14, 2025
Locked-chess-official commentedon Oct 14, 2025
For example,
aa.__init__want's to import modulebbfrom its__path__, but there is nobbthere, so it failed. It means that the code inaa.__init__failed. Any exceptions raised from the "__init__.py" will leads to that the module failed to load, so the parent moduleaawon't be insys.modules.FFY00 commentedon Oct 14, 2025
Right, that'll be true for regular packages, I suppose, when the error happens while we are executing the module. We can change the API to take a module spec instead.
I need to have a better look at this.
Thanks for the feedback!
Locked-chess-official commentedon Oct 16, 2025
Now there is another bug here:
brettcannon commentedon Nov 7, 2025
Is this basically a spec-like replacement for https://docs.python.org/3/library/pkgutil.html#pkgutil.iter_importers ?
Locked-chess-official commentedon Nov 8, 2025
Seems not.
pkgutil.iter_importersfind the finders instead of submodules. More exactly,pkgutil.iter_importerscan simplify the logic to find "which finder find the parent package", but how to find the list of submodules depends on the finder itself.brettcannon commentedon Nov 14, 2025
I'm fine with the idea, although we might want to use this as an opportunity to introduce protocols to importlib to move away from ABCs.
pythongh-139899: Introduce MetaPathFinder.discover and PathEntryFinde…
Sachaa-Thanasius commentedon Jan 14, 2026
Would it be more accurate to say that this could act as a spec-like replacement for
pkgutil.iter_modulesand the non-standardfinder.iter_modulesAPI it works with?If so, there may be an opportunity here to further advance #64138 by deprecating
pkgutil.iter_modulesand potentiallypkgutil.walk_packageswhile also turning them into thin wrappers aroundMetaPathFinder.discovercalls. Assuming I understand this correctly, it would be much clearer how users could replace usage of those functions with this new API in their code, at least compared to now, imo.Merge branch 'main' into pythongh-139899
gh-139899: Introduce MetaPathFinder.discover and PathEntryFinder.disc…
FFY00 commentedon Feb 25, 2026
Implemented in GH-139900.
Let's track the conversion to protocols in GH-82963, as it is not trivial, and whatever we do must follow the approach that will be decided there.
pythongh-139899: Introduce MetaPathFinder.discover and PathEntryFinde…
pythongh-139899: Introduce MetaPathFinder.discover and PathEntryFinde…