]> granicus.if.org Git - python/commitdiff
Issue #17211: Yield a namedtuple in pkgutil.
authorEric Snow <ericsnowcurrently@gmail.com>
Thu, 8 Sep 2016 01:37:17 +0000 (18:37 -0700)
committerEric Snow <ericsnowcurrently@gmail.com>
Thu, 8 Sep 2016 01:37:17 +0000 (18:37 -0700)
Patch by Ramchandra Apte.

Doc/library/pkgutil.rst
Lib/pkgutil.py
Lib/test/test_pkgutil.py
Lib/test/test_runpy.py
Misc/NEWS

index c5ba8d8050f33ada07983896664b7e3bea595bbb..1d038f9cca8b5f6402ee0a310acf408648c46d9d 100644 (file)
 This module provides utilities for the import system, in particular package
 support.
 
+.. class:: ModuleInfo(module_finder, name, ispkg)
+
+    A namedtuple that holds a brief summary of a module's info.
+
 
 .. function:: extend_path(path, name)
 
@@ -139,7 +143,7 @@ support.
 
 .. function:: iter_modules(path=None, prefix='')
 
-   Yields ``(module_finder, name, ispkg)`` for all submodules on *path*, or, if
+   Yields :class:`ModuleInfo` for all submodules on *path*, or, if
    *path* is ``None``, all top-level modules on ``sys.path``.
 
    *path* should be either ``None`` or a list of paths to look for modules in.
@@ -160,7 +164,7 @@ support.
 
 .. function:: walk_packages(path=None, prefix='', onerror=None)
 
-   Yields ``(module_finder, name, ispkg)`` for all modules recursively on
+   Yields :class:`ModuleInfo` for all modules recursively on
    *path*, or, if *path* is ``None``, all accessible modules.
 
    *path* should be either ``None`` or a list of paths to look for modules in.
index 7fc356c9ae8d2965c6865e1d48909f6724035c47..e37ad4519666c16da750c39e5dda72a39145bc51 100644 (file)
@@ -1,5 +1,6 @@
 """Utilities to support packages."""
 
+from collections import namedtuple
 from functools import singledispatch as simplegeneric
 import importlib
 import importlib.util
@@ -14,9 +15,14 @@ __all__ = [
     'get_importer', 'iter_importers', 'get_loader', 'find_loader',
     'walk_packages', 'iter_modules', 'get_data',
     'ImpImporter', 'ImpLoader', 'read_code', 'extend_path',
+    'ModuleInfo',
 ]
 
 
+ModuleInfo = namedtuple('ModuleInfo', 'module_finder name ispkg')
+ModuleInfo.__doc__ = 'A namedtuple with minimal info about a module.'
+
+
 def _get_spec(finder, name):
     """Return the finder-specific module spec."""
     # Works with legacy finders.
@@ -45,7 +51,7 @@ def read_code(stream):
 
 
 def walk_packages(path=None, prefix='', onerror=None):
-    """Yields (module_finder, name, ispkg) for all modules recursively
+    """Yields ModuleInfo for all modules recursively
     on path, or, if path is None, all accessible modules.
 
     'path' should be either None or a list of paths to look for
@@ -78,31 +84,31 @@ def walk_packages(path=None, prefix='', onerror=None):
             return True
         m[p] = True
 
-    for importer, name, ispkg in iter_modules(path, prefix):
-        yield importer, name, ispkg
+    for info in iter_modules(path, prefix):
+        yield info
 
-        if ispkg:
+        if info.ispkg:
             try:
-                __import__(name)
+                __import__(info.name)
             except ImportError:
                 if onerror is not None:
-                    onerror(name)
+                    onerror(info.name)
             except Exception:
                 if onerror is not None:
-                    onerror(name)
+                    onerror(info.name)
                 else:
                     raise
             else:
-                path = getattr(sys.modules[name], '__path__', None) or []
+                path = getattr(sys.modules[info.name], '__path__', None) or []
 
                 # don't traverse path items we've seen before
                 path = [p for p in path if not seen(p)]
 
-                yield from walk_packages(path, name+'.', onerror)
+                yield from walk_packages(path, info.name+'.', onerror)
 
 
 def iter_modules(path=None, prefix=''):
-    """Yields (module_finder, name, ispkg) for all submodules on path,
+    """Yields ModuleInfo for all submodules on path,
     or, if path is None, all top-level modules on sys.path.
 
     'path' should be either None or a list of paths to look for
@@ -111,7 +117,6 @@ def iter_modules(path=None, prefix=''):
     'prefix' is a string to output on the front of every module name
     on output.
     """
-
     if path is None:
         importers = iter_importers()
     else:
@@ -122,7 +127,7 @@ def iter_modules(path=None, prefix=''):
         for name, ispkg in iter_importer_modules(i, prefix):
             if name not in yielded:
                 yielded[name] = 1
-                yield i, name, ispkg
+                yield ModuleInfo(i, name, ispkg)
 
 
 @simplegeneric
index ae2aa1bcea54ae274a518e9044f5acb7a3a1e41b..fc04dcfd7fc41435122444451842e216096ebc92 100644 (file)
@@ -81,8 +81,9 @@ class PkgutilTests(unittest.TestCase):
         self.assertEqual(res2, RESOURCE_DATA)
 
         names = []
-        for loader, name, ispkg in pkgutil.iter_modules([zip_file]):
-            names.append(name)
+        for moduleinfo in pkgutil.iter_modules([zip_file]):
+            self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo)
+            names.append(moduleinfo.name)
         self.assertEqual(names, ['test_getdata_zipfile'])
 
         del sys.path[0]
index db55db702bffbba758e70784f24b5155a3dfeb9c..02b4d62567e5fadbf209e9f65e1ef0a4c0cf3e5f 100644 (file)
@@ -577,13 +577,14 @@ from ..uncle.cousin import nephew
         self.addCleanup(self._del_pkg, pkg_dir)
         for depth in range(2, max_depth+1):
             self._add_relative_modules(pkg_dir, "", depth)
-        for finder, mod_name, ispkg in pkgutil.walk_packages([pkg_dir]):
-            self.assertIsInstance(finder,
+        for moduleinfo in pkgutil.walk_packages([pkg_dir]):
+            self.assertIsInstance(moduleinfo, pkgutil.ModuleInfo)
+            self.assertIsInstance(moduleinfo.module_finder,
                                   importlib.machinery.FileFinder)
-            if ispkg:
-                expected_packages.remove(mod_name)
+            if moduleinfo.ispkg:
+                expected_packages.remove(moduleinfo.name)
             else:
-                expected_modules.remove(mod_name)
+                expected_modules.remove(moduleinfo.name)
         self.assertEqual(len(expected_packages), 0, expected_packages)
         self.assertEqual(len(expected_modules), 0, expected_modules)
 
index c60e8708034fb382070b6e4ca105cdd5598e0c87..0ea22a940cf9fd45a44d23f2933107c3f88b985a 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -7845,6 +7845,9 @@ Library
 - Issue #16809: Tkinter's splitlist() and split() methods now accept Tcl_Obj
   argument.
 
+- Issue #17211: Yield a namedtuple in pkgutil.
+  Patch by Ramchandra Apte.
+
 - Issue #18324: set_payload now correctly handles binary input.  This also
   supersedes the previous fixes for #14360, #1717, and #16564.