# HG changeset patch # Parent 83fa2e9b0038db72dc29be5fbcfcb126841c3b05 pkgutil.iter_modules() and walk_packages() now yield built-in modules diff -r 83fa2e9b0038 Doc/library/pkgutil.rst --- a/Doc/library/pkgutil.rst Mon Nov 02 00:39:56 2015 -0500 +++ b/Doc/library/pkgutil.rst Mon Nov 02 10:39:35 2015 +0000 @@ -140,7 +140,8 @@ .. function:: iter_modules(path=None, prefix='') Yields ``(module_finder, name, ispkg)`` for all submodules on *path*, or, if - path is ``None``, all top-level modules on ``sys.path``. + *path* is ``None``, all built-in modules and top-level modules on + ``sys.path``. *path* should be either ``None`` or a list of paths to look for modules in. @@ -150,13 +151,17 @@ Only works for a :term:`finder` which defines an ``iter_modules()`` method. This interface is non-standard, so the module also provides - implementations for :class:`importlib.machinery.FileFinder` and + implementations for :class:`importlib.machinery.FileFinder`, + :class:`~importlib.machinery.BuiltinImporter` and :class:`zipimport.zipimporter`. .. versionchanged:: 3.3 Updated to be based directly on :mod:`importlib` rather than relying on the package internal PEP 302 import emulation. + .. versionchanged:: 3.6 + Now yields built-in modules. + .. function:: walk_packages(path=None, prefix='', onerror=None) @@ -189,13 +194,17 @@ Only works for a :term:`finder` which defines an ``iter_modules()`` method. This interface is non-standard, so the module also provides - implementations for :class:`importlib.machinery.FileFinder` and + implementations for :class:`importlib.machinery.FileFinder`, + :class:`~importlib.machinery.BuiltinImporter` and :class:`zipimport.zipimporter`. .. versionchanged:: 3.3 Updated to be based directly on :mod:`importlib` rather than relying on the package internal PEP 302 import emulation. + .. versionchanged:: 3.6 + Now yields built-in modules. + .. function:: get_data(package, resource) diff -r 83fa2e9b0038 Doc/whatsnew/3.6.rst --- a/Doc/whatsnew/3.6.rst Mon Nov 02 00:39:56 2015 -0500 +++ b/Doc/whatsnew/3.6.rst Mon Nov 02 10:39:35 2015 +0000 @@ -113,6 +113,13 @@ Storchaka in :issue:`24164`.) +pkgutil +------- + +The :func:`~pkgutil.iter_modules` and :func:`~pkgutil.walk_packages` +functions now yield built-in modules. + + rlcomplete ---------- @@ -226,6 +233,9 @@ * The :mod:`imp` module now raises a :exc:`DeprecationWarning` instead of :exc:`PendingDeprecationWarning`. +* :func:`pkgutil.iter_modules` and :func:`~pkgutil.walk_packages` now + yield built-in modules. + Changes in the C API -------------------- diff -r 83fa2e9b0038 Lib/pkgutil.py --- a/Lib/pkgutil.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/pkgutil.py Mon Nov 02 10:39:35 2015 +0000 @@ -1,6 +1,6 @@ """Utilities to support packages.""" -from functools import singledispatch as simplegeneric +from functools import singledispatch import importlib import importlib.util import importlib.machinery @@ -103,7 +103,8 @@ def iter_modules(path=None, prefix=''): """Yields (module_loader, name, ispkg) for all submodules on path, - or, if path is None, all top-level modules on sys.path. + or, if path is None, all built-in modules and top-level modules on + sys.path. 'path' should be either None or a list of paths to look for modules in. @@ -125,14 +126,20 @@ yield i, name, ispkg -@simplegeneric +@singledispatch def iter_importer_modules(importer, prefix=''): - if not hasattr(importer, 'iter_modules'): + if hasattr(importer, 'iter_modules'): + return importer.iter_modules(prefix) + elif importer is importlib.machinery.BuiltinImporter: + # Not instantiated because it has no instance methods, therefore not + # handled by @singledispatch + return _iter_builtin_modules(importer, prefix) + else: return [] - return importer.iter_modules(prefix) # Implement a file walker for the normal importlib path hook +@iter_importer_modules.register(importlib.machinery.FileFinder) def _iter_file_finder_modules(importer, prefix=''): if importer.path is None or not os.path.isdir(importer.path): return @@ -173,8 +180,11 @@ yielded[modname] = 1 yield prefix + modname, ispkg -iter_importer_modules.register( - importlib.machinery.FileFinder, _iter_file_finder_modules) + +@iter_importer_modules.register(importlib.machinery.BuiltinImporter) +def _iter_builtin_modules(importer, prefix=''): + for modname in sys.builtin_module_names: + yield (prefix + modname, False) def _import_imp(): @@ -360,6 +370,7 @@ import zipimport from zipimport import zipimporter + @iter_importer_modules.register(zipimporter) def iter_zipimport_modules(importer, prefix=''): dirlist = sorted(zipimport._zip_directory_cache[importer.archive]) _prefix = importer.prefix @@ -388,8 +399,6 @@ yielded[modname] = 1 yield prefix + modname, False - iter_importer_modules.register(zipimporter, iter_zipimport_modules) - except ImportError: pass diff -r 83fa2e9b0038 Lib/pydoc.py --- a/Lib/pydoc.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/pydoc.py Mon Nov 02 10:39:35 2015 +0000 @@ -2060,19 +2060,6 @@ def run(self, callback, key=None, completer=None, onerror=None): if key: key = key.lower() self.quit = False - seen = {} - - for modname in sys.builtin_module_names: - if modname != '__main__': - seen[modname] = 1 - if key is None: - callback(None, modname, '') - else: - name = __import__(modname).__doc__ or '' - desc = name.split('\n')[0] - name = modname + ' - ' + desc - if name.lower().find(key) >= 0: - callback(None, modname, desc) for importer, modname, ispkg in pkgutil.walk_packages(onerror=onerror): if self.quit: @@ -2087,6 +2074,7 @@ # raised by tests for bad coding cookies or BOM continue loader = spec.loader + source = None if hasattr(loader, 'get_source'): try: source = loader.get_source(modname) @@ -2094,12 +2082,13 @@ if onerror: onerror(modname) continue + if source is not None: desc = source_synopsis(io.StringIO(source)) or '' if hasattr(loader, 'get_filename'): path = loader.get_filename(modname) else: path = None - else: + else: # No get_source(), or source is None (built-in module) try: module = importlib._bootstrap._load(spec) except ImportError: diff -r 83fa2e9b0038 Lib/test/test_pkgutil.py --- a/Lib/test/test_pkgutil.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/test/test_pkgutil.py Mon Nov 02 10:39:35 2015 +0000 @@ -2,6 +2,7 @@ import unittest import sys import importlib +from importlib.machinery import BuiltinImporter from importlib.util import spec_from_file_location import pkgutil import os @@ -101,6 +102,18 @@ for t in pkgutil.walk_packages(path=[self.dirname]): self.fail("unexpected package found") + def test_iter_builtin(self): + # iter_modules() should yield built-in modules + for [finder, name, ispkg] in pkgutil.iter_modules(): + if name == "sys": + break + else: + self.fail("sys module missing") + # BuiltinImporter only has class methods, so uninstantiated is okay + self.assertTrue(isinstance(finder, BuiltinImporter) or + issubclass(finder, BuiltinImporter)) + self.assertFalse(ispkg) + class PkgutilPEP302Tests(unittest.TestCase): class MyTestLoader(object): diff -r 83fa2e9b0038 Misc/NEWS --- a/Misc/NEWS Mon Nov 02 00:39:56 2015 -0500 +++ b/Misc/NEWS Mon Nov 02 10:39:35 2015 +0000 @@ -66,6 +66,8 @@ Library ------- +- pkgutil.iter_modules() and walk_packages() now yield built-in modules. + - Issue #18973: Command-line interface of the calendar module now uses argparse instead of optparse.