# HG changeset patch # Parent 83fa2e9b0038db72dc29be5fbcfcb126841c3b05 Issue #25533: Support iterating built-in modules; add sys.get_frozen_modules * Add new sys.get_frozen_modules() function * Add "builtins" flag to pkgutil.iter_modules() and walk_packages(), to enable yielding built-in and frozen modules * Frozen submodules not supported except for walk_packages() with path=None * Update documentation regarding the frozen modules table * "ctypes" documentation was using Python 2 bytes-str equivalence. * Change demo frozen modules to only print output when run as "__main__", in order to keep sensible pkgutil behaviour * test_importlib.frozen.test_loader() broken due to relying on output printed by the frozen modules diff -r 83fa2e9b0038 Doc/c-api/import.rst --- a/Doc/c-api/import.rst Mon Nov 02 00:39:56 2015 -0500 +++ b/Doc/c-api/import.rst Fri Nov 13 11:58:12 2015 +0000 @@ -277,7 +277,7 @@ }; -.. c:var:: struct _frozen* PyImport_FrozenModules +.. c:var:: const struct _frozen* PyImport_FrozenModules This pointer is initialized to point to an array of :c:type:`struct _frozen` records, terminated by one whose members are all *NULL* or zero. When a frozen diff -r 83fa2e9b0038 Doc/library/ctypes.rst --- a/Doc/library/ctypes.rst Mon Nov 02 00:39:56 2015 -0500 +++ b/Doc/library/ctypes.rst Fri Nov 13 11:58:12 2015 +0000 @@ -1094,14 +1094,15 @@ hit the NULL entry:: >>> for item in table: - ... print(item.name, item.size) ... if item.name is None: ... break + ... print(item.name.decode("ascii"), item.size) ... - __hello__ 104 - __phello__ -104 - __phello__.spam 104 - None 0 + _frozen_importlib 31758 + _frozen_importlib_external 41218 + __hello__ 175 + __phello__ -175 + __phello__.spam 175 >>> The fact that standard Python has a frozen module and a frozen package 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 Fri Nov 13 11:58:12 2015 +0000 @@ -137,10 +137,12 @@ on the package internal PEP 302 import emulation. -.. function:: iter_modules(path=None, prefix='') +.. function:: iter_modules(path=None, prefix='', *, builtins=False) 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 top-level modules on ``sys.path``. + If *builtins* is true, top-level built-in and frozen modules will also + be included if *path* is ``None``. Frozen submodules are not included. *path* should be either ``None`` or a list of paths to look for modules in. @@ -150,18 +152,26 @@ 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`, + :class:`~importlib.machinery.FrozenImporter`, 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. + .. versionadded:: 3.6 + The *builtins* flag. -.. function:: walk_packages(path=None, prefix='', onerror=None) + +.. function:: walk_packages(path=None, prefix='', onerror=None, *, \ + builtins=False) Yields ``(module_finder, name, ispkg)`` for all modules recursively on - *path*, or, if path is ``None``, all accessible modules. + *path*, or, if *path* is ``None``, all accessible modules. + Built-in modules, frozen modules, and frozen submodules are included if + *builtins* is true and *path* is ``None``. *path* should be either ``None`` or a list of paths to look for modules in. @@ -180,7 +190,7 @@ Examples:: # list all modules python can access - walk_packages() + walk_packages(builtins=True) # list all submodules of ctypes walk_packages(ctypes.__path__, ctypes.__name__ + '.') @@ -189,13 +199,18 @@ 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`, + :class:`~importlib.machinery.FrozenImporter` 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. + .. versionadded:: 3.6 + The *builtins* flag. + .. function:: get_data(package, resource) diff -r 83fa2e9b0038 Doc/library/sys.rst --- a/Doc/library/sys.rst Mon Nov 02 00:39:56 2015 -0500 +++ b/Doc/library/sys.rst Fri Nov 13 11:58:12 2015 +0000 @@ -588,6 +588,16 @@ for details.) Use it only for debugging purposes. +.. function:: get_frozen_modules() + + Returns a tuple listing information about each frozen module, + including submodules of frozen packages. Each item is a further tuple + (*name*, *ispkg*), where *name* is the full module name, and *ispkg* + is :const:`True` for a package, or :const:`False` otherwise. + + .. versionadded:: 3.6 + + .. data:: hash_info A :term:`struct sequence` giving parameters of the numeric hash 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 Fri Nov 13 11:58:12 2015 +0000 @@ -113,6 +113,14 @@ Storchaka in :issue:`24164`.) +pkgutil +------- + +The :func:`~pkgutil.iter_modules` and :func:`~pkgutil.walk_packages` +functions now accept a *builtins* flag which enables yielding built-in and +frozen modules. (Contributed by Martin Panter in :issue:`25533`.) + + rlcomplete ---------- @@ -121,6 +129,13 @@ (Contributed by Serhiy Storchaka in :issue:`25011` and :issue:`25209`.) +sys +--- + +A new :func:`~sys.get_frozen_modules` function lists information about +frozen modules. (Contributed by Martin Panter in :issue:`25533`.) + + urllib.robotparser ------------------ diff -r 83fa2e9b0038 Lib/ctypes/test/test_values.py --- a/Lib/ctypes/test/test_values.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/ctypes/test/test_values.py Fri Nov 13 11:58:12 2015 +0000 @@ -45,6 +45,9 @@ # array is marked by an entry containing a NULL name and zero # size. + # Updates to this test should be synchronized with the "Accessing + # values exported from dlls" section in /Doc/library/ctypes.rst. + # In standard Python, this table contains a __hello__ # module, and a __phello__ package containing a spam # module. @@ -80,9 +83,9 @@ continue items.append((entry.name, entry.size)) - expected = [(b"__hello__", 161), - (b"__phello__", -161), - (b"__phello__.spam", 161), + expected = [(b"__hello__", 175), + (b"__phello__", -175), + (b"__phello__.spam", 175), ] self.assertEqual(items, expected) diff -r 83fa2e9b0038 Lib/pkgutil.py --- a/Lib/pkgutil.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/pkgutil.py Fri Nov 13 11:58:12 2015 +0000 @@ -1,9 +1,9 @@ """Utilities to support packages.""" -from functools import singledispatch as simplegeneric +from functools import singledispatch import importlib import importlib.util -import importlib.machinery +from importlib.machinery import FileFinder, BuiltinImporter, FrozenImporter import os import os.path import sys @@ -44,9 +44,11 @@ return marshal.load(stream) -def walk_packages(path=None, prefix='', onerror=None): +def walk_packages(path=None, prefix='', onerror=None, *, builtins=False): """Yields (module_loader, name, ispkg) for all modules recursively - on path, or, if path is None, all accessible modules. + on path, or, if path is None, all accessible modules. Built-in modules, + frozen modules, and frozen submodules are included if 'builtins' is true + and 'path' is None. 'path' should be either None or a list of paths to look for modules in. @@ -67,7 +69,7 @@ Examples: # list all modules python can access - walk_packages() + walk_packages(builtins=True) # list all submodules of ctypes walk_packages(ctypes.__path__, ctypes.__name__+'.') @@ -78,7 +80,8 @@ return True m[p] = True - for importer, name, ispkg in iter_modules(path, prefix): + modules = iter_modules(path, prefix, builtins=builtins) + for importer, name, ispkg in modules: yield importer, name, ispkg if ispkg: @@ -93,17 +96,33 @@ else: raise else: - path = getattr(sys.modules[name], '__path__', None) or [] + name_prefix = name+'.' + if builtins and (isinstance(importer, FrozenImporter) or + isinstance(importer, type) and + issubclass(importer, FrozenImporter)): + yield from _walk_frozen_package(prefix, name_prefix) + else: + path = getattr(sys.modules[name], '__path__', None) or [] - # don't traverse path items we've seen before - path = [p for p in path if not seen(p)] + # 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, name_prefix, onerror) -def iter_modules(path=None, prefix=''): +def _walk_frozen_package(prefix, name_prefix): + # Use sorted() to ensure that submodules come after their parent package + for name, ispkg in sorted(sys.get_frozen_modules()): + if not name.startswith(name_prefix): + continue + yield (FrozenImporter, prefix + name, ispkg) + + +def iter_modules(path=None, prefix='', *, builtins=False): """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 top-level modules on sys.path. If 'builtins' is + true, top-level built-in and frozen modules will also be included if + 'path' is None. Frozen submodules are not included. 'path' should be either None or a list of paths to look for modules in. @@ -119,21 +138,30 @@ yielded = {} for i in importers: - for name, ispkg in iter_importer_modules(i, prefix): + modules = iter_importer_modules(i, prefix, builtins=builtins) + for name, ispkg in modules: if name not in yielded: yielded[name] = 1 yield i, name, ispkg -@simplegeneric -def iter_importer_modules(importer, prefix=''): - if not hasattr(importer, 'iter_modules'): - return [] - return importer.iter_modules(prefix) +@singledispatch +def iter_importer_modules(importer, prefix='', *, builtins=False): + if hasattr(importer, 'iter_modules'): + return importer.iter_modules(prefix) + if builtins and isinstance(importer, type): + # importer may not be instantated if it has no instance methods; + # these cases are not handled by @singledispatch + if issubclass(importer, BuiltinImporter): + return _iter_builtin_modules(importer, prefix, builtins=builtins) + if issubclass(importer, FrozenImporter): + return _iter_frozen_modules(importer, prefix, builtins=builtins) + return [] # Implement a file walker for the normal importlib path hook -def _iter_file_finder_modules(importer, prefix=''): +@iter_importer_modules.register(FileFinder) +def _iter_file_finder_modules(importer, prefix='', *, builtins=False): if importer.path is None or not os.path.isdir(importer.path): return @@ -173,8 +201,23 @@ yielded[modname] = 1 yield prefix + modname, ispkg -iter_importer_modules.register( - importlib.machinery.FileFinder, _iter_file_finder_modules) + +@iter_importer_modules.register(BuiltinImporter) +def _iter_builtin_modules(importer, prefix='', *, builtins=False): + if not builtins: + return + for modname in sys.builtin_module_names: + yield (prefix + modname, False) + + +@iter_importer_modules.register(FrozenImporter) +def _iter_frozen_modules(importer, prefix='', *, builtins=False): + if not builtins: + return + for modname, ispkg in sys.get_frozen_modules(): + if '.' in modname: + continue + yield (prefix + modname, ispkg) def _import_imp(): @@ -360,7 +403,8 @@ import zipimport from zipimport import zipimporter - def iter_zipimport_modules(importer, prefix=''): + @iter_importer_modules.register(zipimporter) + def iter_zipimport_modules(importer, prefix='', *, builtins=False): dirlist = sorted(zipimport._zip_directory_cache[importer.archive]) _prefix = importer.prefix plen = len(_prefix) @@ -388,8 +432,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 Fri Nov 13 11:58:12 2015 +0000 @@ -2060,21 +2060,8 @@ 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): + for importer, modname, ispkg in pkgutil.walk_packages(builtins=True, onerror=onerror): if self.quit: break @@ -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_importlib/frozen/test_loader.py --- a/Lib/test/test_importlib/frozen/test_loader.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/test/test_importlib/frozen/test_loader.py Fri Nov 13 11:58:12 2015 +0000 @@ -30,24 +30,23 @@ def test_module(self): name = '__hello__' module, output = self.exec_module(name) - check = {'__name__': name} + check = {'__name__': name, 'initialized': True} for attr, value in check.items(): self.assertEqual(getattr(module, attr), value) - self.assertEqual(output, 'Hello world!\n') self.assertTrue(hasattr(module, '__spec__')) def test_package(self): name = '__phello__' module, output = self.exec_module(name) - check = {'__name__': name} + check = {'__name__': name, 'initialized': True} for attr, value in check.items(): attr_value = getattr(module, attr) self.assertEqual(attr_value, value, 'for {name}.{attr}, {given!r} != {expected!r}'.format( name=name, attr=attr, given=attr_value, expected=value)) - self.assertEqual(output, 'Hello world!\n') + @unittest.expectedFailure # Modules only output when run as __main__ def test_lacking_parent(self): name = '__phello__.spam' with util.uncache('__phello__'): @@ -94,21 +93,21 @@ class LoaderTests(abc.LoaderTests): def test_module(self): - with util.uncache('__hello__'), captured_stdout() as stdout: + with util.uncache('__hello__'): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) module = self.machinery.FrozenImporter.load_module('__hello__') check = {'__name__': '__hello__', '__package__': '', '__loader__': self.machinery.FrozenImporter, + 'initialized': True, } for attr, value in check.items(): self.assertEqual(getattr(module, attr), value) - self.assertEqual(stdout.getvalue(), 'Hello world!\n') self.assertFalse(hasattr(module, '__file__')) def test_package(self): - with util.uncache('__phello__'), captured_stdout() as stdout: + with util.uncache('__phello__'): with warnings.catch_warnings(): warnings.simplefilter('ignore', DeprecationWarning) module = self.machinery.FrozenImporter.load_module('__phello__') @@ -116,15 +115,16 @@ '__package__': '__phello__', '__path__': [], '__loader__': self.machinery.FrozenImporter, + 'initialized': True, } for attr, value in check.items(): attr_value = getattr(module, attr) self.assertEqual(attr_value, value, "for __phello__.%s, %r != %r" % (attr, attr_value, value)) - self.assertEqual(stdout.getvalue(), 'Hello world!\n') self.assertFalse(hasattr(module, '__file__')) + @unittest.expectedFailure # Modules only output when run as __main__ def test_lacking_parent(self): with util.uncache('__phello__', '__phello__.spam'), \ captured_stdout() as stdout: @@ -143,6 +143,7 @@ self.assertEqual(stdout.getvalue(), 'Hello world!\n') self.assertFalse(hasattr(module, '__file__')) + @unittest.expectedFailure # Modules only output when run as __main__ def test_module_reuse(self): with util.uncache('__hello__'), captured_stdout() as stdout: with warnings.catch_warnings(): @@ -190,12 +191,10 @@ def test_get_code(self): # Make sure that the code object is good. name = '__hello__' - with captured_stdout() as stdout: - code = self.machinery.FrozenImporter.get_code(name) - mod = types.ModuleType(name) - exec(code, mod.__dict__) - self.assertTrue(hasattr(mod, 'initialized')) - self.assertEqual(stdout.getvalue(), 'Hello world!\n') + code = self.machinery.FrozenImporter.get_code(name) + mod = types.ModuleType(name) + exec(code, mod.__dict__) + self.assertTrue(hasattr(mod, 'initialized')) def test_get_source(self): # Should always return None. 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 Fri Nov 13 11:58:12 2015 +0000 @@ -2,6 +2,7 @@ import unittest import sys import importlib +from importlib.machinery import BuiltinImporter, FrozenImporter from importlib.util import spec_from_file_location import pkgutil import os @@ -101,6 +102,37 @@ for t in pkgutil.walk_packages(path=[self.dirname]): self.fail("unexpected package found") + def test_iter_builtins(self): + self.check_builtin_iter(pkgutil.iter_modules) + + def test_walk_builtins(self): + extra = {"__phello__.spam": (FrozenImporter, False)} + self.check_builtin_iter(pkgutil.walk_packages, extra) + + def check_builtin_iter(self, func, extra=dict()): + to_test = { + "sys": (BuiltinImporter, False), + "__hello__": (FrozenImporter, False), + "__phello__": (FrozenImporter, True), + } + to_test.update(extra) + modules = func(builtins=True) + # Stop iterating early to avoid scanning arbitrary sys.path entries + while to_test: + [finder, name, ispkg] = next(modules) + with self.subTest(name): + expected = to_test.pop(name, None) + if expected is None: + continue + [finder_class, expected_ispkg] = expected + if isinstance(finder, type): + # BuiltinImporter and FrozenImporter only have class + # methods, so uninstantated is okay + self.assertTrue(issubclass(finder, finder_class)) + else: + self.assertIsInstance(finder, finder_class) + self.assertEqual(ispkg, expected_ispkg) + class PkgutilPEP302Tests(unittest.TestCase): class MyTestLoader(object): diff -r 83fa2e9b0038 Lib/test/test_pydoc.py --- a/Lib/test/test_pydoc.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/test/test_pydoc.py Fri Nov 13 11:58:12 2015 +0000 @@ -382,8 +382,8 @@ a given path. """ default_path = path or [os.path.dirname(__file__)] - def wrapper(path=None, prefix='', onerror=None): - return walk_packages(path or default_path, prefix, onerror) + def wrapper(path=None, *pos, **kw): + return walk_packages(path or default_path, *pos, **kw) return wrapper @contextlib.contextmanager diff -r 83fa2e9b0038 Lib/test/test_sys.py --- a/Lib/test/test_sys.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Lib/test/test_sys.py Fri Nov 13 11:58:12 2015 +0000 @@ -805,6 +805,22 @@ rc, stdout, stderr = assert_python_ok('-c', code) self.assertEqual(stdout.rstrip(), b'True') + def test_frozen_modules(self): + modules = sys.get_frozen_modules() + self.assertIsInstance(modules, tuple) + to_test = { + '__hello__': False, + '__phello__': True, + '__phello__.spam': False, + } + for [name, ispkg] in modules: + with self.subTest(name): + expected = to_test.pop(name, None) + if expected is None: + continue + self.assertEqual(ispkg, expected) + self.assertFalse(to_test) + @test.support.cpython_only class SizeofTest(unittest.TestCase): diff -r 83fa2e9b0038 Misc/NEWS --- a/Misc/NEWS Mon Nov 02 00:39:56 2015 -0500 +++ b/Misc/NEWS Fri Nov 13 11:58:12 2015 +0000 @@ -66,6 +66,11 @@ Library ------- +- Issue #25533: Added "builtins" flag for pkgutil.iter_modules() and + walk_packages() to include built-in and frozen modules. Added + sys.get_frozen_modules() to list frozen module information. Simplified + pydoc to use the new functionality. + - Issue #18973: Command-line interface of the calendar module now uses argparse instead of optparse. diff -r 83fa2e9b0038 Python/frozen.c --- a/Python/frozen.c Mon Nov 02 00:39:56 2015 -0500 +++ b/Python/frozen.c Fri Nov 13 11:58:12 2015 +0000 @@ -14,17 +14,17 @@ the appropriate bytes from M___main__.c. */ static unsigned char M___hello__[] = { - 99,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0, - 0,64,0,0,0,115,20,0,0,0,100,2,0,90,1,0, - 101,2,0,100,0,0,131,1,0,1,100,1,0,83,40,3, - 0,0,0,117,12,0,0,0,72,101,108,108,111,32,119,111, - 114,108,100,33,78,84,40,3,0,0,0,117,4,0,0,0, - 84,114,117,101,117,11,0,0,0,105,110,105,116,105,97,108, - 105,122,101,100,117,5,0,0,0,112,114,105,110,116,40,0, - 0,0,0,40,0,0,0,0,40,0,0,0,0,117,7,0, - 0,0,102,108,97,103,46,112,121,117,8,0,0,0,60,109, - 111,100,117,108,101,62,1,0,0,0,115,2,0,0,0,6, - 1, + 227,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0, + 0,64,0,0,0,115,32,0,0,0,100,0,0,90,0,0, + 101,1,0,100,1,0,107,2,0,114,28,0,101,2,0,100, + 2,0,131,1,0,1,100,3,0,83,41,4,84,218,8,95, + 95,109,97,105,110,95,95,122,12,72,101,108,108,111,32,119, + 111,114,108,100,33,78,41,3,218,11,105,110,105,116,105,97, + 108,105,122,101,100,218,8,95,95,110,97,109,101,95,95,218, + 5,112,114,105,110,116,169,0,114,5,0,0,0,114,5,0, + 0,0,250,20,84,111,111,108,115,47,102,114,101,101,122,101, + 47,102,108,97,103,46,112,121,218,8,60,109,111,100,117,108, + 101,62,1,0,0,0,115,4,0,0,0,6,1,12,1, }; #define SIZE (int)sizeof(M___hello__) diff -r 83fa2e9b0038 Python/sysmodule.c --- a/Python/sysmodule.c Mon Nov 02 00:39:56 2015 -0500 +++ b/Python/sysmodule.c Fri Nov 13 11:58:12 2015 +0000 @@ -1204,6 +1204,45 @@ "is_finalizing()\n\ Return True if Python is exiting."); +static PyObject * +sys_get_frozen_modules(PyObject *self, PyObject *args) +{ + PyObject *list; + const struct _frozen *p; + PyObject *result; + + list = PyList_New(0); + if (list == NULL) { + return NULL; + } + for (p = PyImport_FrozenModules; p->name != NULL; p++) { + PyObject *record; + PyObject *ispkg; + int appended; + + ispkg = PyBool_FromLong(p->size < 0); + record = Py_BuildValue("(sN)", p->name, ispkg); + if (record == NULL) { + Py_DECREF(list); + return NULL; + } + appended = PyList_Append(list, record); + Py_DECREF(record); + if (appended < 0) { + Py_DECREF(list); + return NULL; + } + } + result = PyList_AsTuple(list); + Py_DECREF(list); + return result; +} + +PyDoc_STRVAR(get_frozen_modules_doc, +"get_frozen_modules() -> ((name, ispkg), ...)\n\ +\n\ +Return a tuple of records listing all the frozen modules and submodules."); + static PyMethodDef sys_methods[] = { /* Might as well keep this in alphabetic order */ @@ -1284,6 +1323,8 @@ set_coroutine_wrapper_doc}, {"get_coroutine_wrapper", sys_get_coroutine_wrapper, METH_NOARGS, get_coroutine_wrapper_doc}, + {"get_frozen_modules", sys_get_frozen_modules, METH_NOARGS, + get_frozen_modules_doc}, {NULL, NULL} /* sentinel */ }; diff -r 83fa2e9b0038 Tools/freeze/flag.py --- a/Tools/freeze/flag.py Mon Nov 02 00:39:56 2015 -0500 +++ b/Tools/freeze/flag.py Fri Nov 13 11:58:12 2015 +0000 @@ -1,2 +1,3 @@ initialized = True -print("Hello world!") +if __name__ == "__main__": + print("Hello world!")