From 809924990e41ae6daf68384210a6f0f9ace1a283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Fri, 10 Oct 2025 14:03:51 +0100 Subject: [PATCH 1/6] gh-139899: Introduce MetaPathFinder.discover and PathEntryFinder.discover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Doc/library/importlib.rst | 20 +++++++++++ Lib/importlib/_bootstrap_external.py | 36 +++++++++++++++++++ Lib/importlib/abc.py | 17 +++++++++ ...-10-10-14-08-58.gh-issue-139899.09leRY.rst | 2 ++ 4 files changed, 75 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 3f0a54ac535cd6..fcfd8fb2152c13 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -275,6 +275,16 @@ ABC hierarchy:: .. versionchanged:: 3.4 Returns ``None`` when called instead of :data:`NotImplemented`. + .. method:: discover(parent=None) + + An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will + search for top-level modules. + + Returns an iterable of possible specs. + + .. versionadded:: next + .. class:: PathEntryFinder @@ -307,6 +317,16 @@ ABC hierarchy:: :meth:`importlib.machinery.PathFinder.invalidate_caches` when invalidating the caches of all cached finders. + .. method:: discover(parent=None) + + An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will + search for top-level modules. + + Returns an iterable of possible specs. + + .. versionadded:: next + .. class:: Loader diff --git a/Lib/importlib/_bootstrap_external.py b/Lib/importlib/_bootstrap_external.py index 2f9307cba4f086..21d60d3393e1ca 100644 --- a/Lib/importlib/_bootstrap_external.py +++ b/Lib/importlib/_bootstrap_external.py @@ -1323,6 +1323,21 @@ def find_spec(cls, fullname, path=None, target=None): else: return spec + @classmethod + def discover(cls, parent=None): + if parent is None: + path = sys.path + else: + path = parent.submodule_search_locations + + for entry in path: + if not isinstance(entry, str): + continue + if (finder := cls._path_importer_cache(entry)) is None: + continue + if discover := getattr(finder, 'discover', None): + yield from discover(parent) + @staticmethod def find_distributions(*args, **kwargs): """ @@ -1472,6 +1487,27 @@ def path_hook_for_FileFinder(path): return path_hook_for_FileFinder + def _find_children(self): + for entry in _os.scandir(self.path): + if entry.name == _PYCACHE: + continue + # packages + if entry.is_dir() and '.' not in entry.name: + yield entry.name + # files + if entry.is_file(): + yield from [ + entry.name.removesuffix(suffix) + for suffix, _ in self._loaders + if entry.name.endswith(suffix) + ] + + def discover(self, parent=None): + module_prefix = f'{parent.name}.' if parent else '' + for child_name in self._find_children(): + if spec := self.find_spec(module_prefix + child_name): + yield spec + def __repr__(self): return f'FileFinder({self.path!r})' diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 5c13432b5bda8c..8d336e737f5dab 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -45,6 +45,15 @@ def invalidate_caches(self): This method is used by importlib.invalidate_caches(). """ + def discover(self, parent=None): + """An optional method which searches for possible specs with given *parent*. + If *parent* is *None*, MetaPathFinder.discover will search for top-level modules. + + Returns an iterable of possible specs. + """ + return () + + _register(MetaPathFinder, machinery.BuiltinImporter, machinery.FrozenImporter, machinery.PathFinder, machinery.WindowsRegistryFinder) @@ -58,6 +67,14 @@ def invalidate_caches(self): This method is used by PathFinder.invalidate_caches(). """ + def discover(self, parent=None): + """An optional method which searches for possible specs with given *parent*. + If *parent* is *None*, PathEntryFinder.discover will search for top-level modules. + + Returns an iterable of possible specs. + """ + return () + _register(PathEntryFinder, machinery.FileFinder) diff --git a/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst b/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst new file mode 100644 index 00000000000000..5851566609188f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst @@ -0,0 +1,2 @@ +Introduced :meth:`MetaPathFinder.discover` and +:meth:`PathEntryFinder.discover`. From 6816705de0c2c6cd7289f405c5e11cbf13e4930e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 10 Dec 2025 12:54:09 +0000 Subject: [PATCH 2/6] Fix doc reference MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Doc/library/importlib.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index fcfd8fb2152c13..5cc5f3e7c3e848 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -278,7 +278,7 @@ ABC hierarchy:: .. method:: discover(parent=None) An optional method which searches for possible specs with given *parent* - module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will + module spec. If *parent* is *None*, :meth:`MetaPathFinder.discover` will search for top-level modules. Returns an iterable of possible specs. From 31d1a8f5510e0f7a53016c7120ea2e1bda46e60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 10 Dec 2025 12:55:50 +0000 Subject: [PATCH 3/6] Remove specific doc references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Doc/library/importlib.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 5cc5f3e7c3e848..53322beaceb501 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -278,8 +278,8 @@ ABC hierarchy:: .. method:: discover(parent=None) An optional method which searches for possible specs with given *parent* - module spec. If *parent* is *None*, :meth:`MetaPathFinder.discover` will - search for top-level modules. + module spec. If *parent* is *None*, ``discover`` will search for top-level + modules. Returns an iterable of possible specs. @@ -320,8 +320,8 @@ ABC hierarchy:: .. method:: discover(parent=None) An optional method which searches for possible specs with given *parent* - module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will - search for top-level modules. + module spec. If *parent* is *None*, ``discover`` will search for top-level + modules. Returns an iterable of possible specs. From 051cd1e5cf99af1ca42610306351955b7601e0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 10 Dec 2025 13:05:29 +0000 Subject: [PATCH 4/6] Fix docstrings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- Lib/importlib/abc.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Lib/importlib/abc.py b/Lib/importlib/abc.py index 8d336e737f5dab..e2d7b74af49bcc 100644 --- a/Lib/importlib/abc.py +++ b/Lib/importlib/abc.py @@ -46,8 +46,9 @@ def invalidate_caches(self): """ def discover(self, parent=None): - """An optional method which searches for possible specs with given *parent*. - If *parent* is *None*, MetaPathFinder.discover will search for top-level modules. + """An optional method which searches for possible specs with given *parent* + module spec. If *parent* is *None*, MetaPathFinder.discover will search + for top-level modules. Returns an iterable of possible specs. """ @@ -68,8 +69,9 @@ def invalidate_caches(self): """ def discover(self, parent=None): - """An optional method which searches for possible specs with given *parent*. - If *parent* is *None*, PathEntryFinder.discover will search for top-level modules. + """An optional method which searches for possible specs with given + *parent* module spec. If *parent* is *None*, PathEntryFinder.discover + will search for top-level modules. Returns an iterable of possible specs. """ From c343d328b90068df086b3482c498130fff305b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 10 Dec 2025 13:08:55 +0000 Subject: [PATCH 5/6] Revert "Remove specific doc references" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 31d1a8f5510e0f7a53016c7120ea2e1bda46e60c. Signed-off-by: Filipe Laíns --- Doc/library/importlib.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst index 53322beaceb501..5cc5f3e7c3e848 100644 --- a/Doc/library/importlib.rst +++ b/Doc/library/importlib.rst @@ -278,8 +278,8 @@ ABC hierarchy:: .. method:: discover(parent=None) An optional method which searches for possible specs with given *parent* - module spec. If *parent* is *None*, ``discover`` will search for top-level - modules. + module spec. If *parent* is *None*, :meth:`MetaPathFinder.discover` will + search for top-level modules. Returns an iterable of possible specs. @@ -320,8 +320,8 @@ ABC hierarchy:: .. method:: discover(parent=None) An optional method which searches for possible specs with given *parent* - module spec. If *parent* is *None*, ``discover`` will search for top-level - modules. + module spec. If *parent* is *None*, :meth:`PathEntryFinder.discover` will + search for top-level modules. Returns an iterable of possible specs. From a324d96647735f19b97656b8f757f14ca78e1e82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20La=C3=ADns?= Date: Wed, 10 Dec 2025 13:10:01 +0000 Subject: [PATCH 6/6] Fix news references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Filipe Laíns --- .../Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst b/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst index 5851566609188f..74635e25b9b748 100644 --- a/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst +++ b/Misc/NEWS.d/next/Library/2025-10-10-14-08-58.gh-issue-139899.09leRY.rst @@ -1,2 +1,2 @@ -Introduced :meth:`MetaPathFinder.discover` and -:meth:`PathEntryFinder.discover`. +Introduced :meth:`importlib.abc.MetaPathFinder.discover` +and :meth:`importlib.abc.PathEntryFinder.discover`.