Commit 32439d6e authored by Eric Snow's avatar Eric Snow

Issue #23911: Move path-based bootstrap code to a separate frozen module.

parent 6b4c63de
...@@ -59,8 +59,13 @@ class Win_ValuesTestCase(unittest.TestCase): ...@@ -59,8 +59,13 @@ class Win_ValuesTestCase(unittest.TestCase):
items = [] items = []
# _frozen_importlib changes size whenever importlib._bootstrap # _frozen_importlib changes size whenever importlib._bootstrap
# changes, so it gets a special case. We should make sure it's # changes, so it gets a special case. We should make sure it's
# found, but don't worry about its size too much. # found, but don't worry about its size too much. The same
_fzn_implib_seen = False # applies to _frozen_importlib_external.
bootstrap_seen = []
bootstrap_expected = (
b'_frozen_importlib',
b'_frozen_importlib_external',
)
for entry in ft: for entry in ft:
# This is dangerous. We *can* iterate over a pointer, but # This is dangerous. We *can* iterate over a pointer, but
# the loop will not terminate (maybe with an access # the loop will not terminate (maybe with an access
...@@ -68,10 +73,10 @@ class Win_ValuesTestCase(unittest.TestCase): ...@@ -68,10 +73,10 @@ class Win_ValuesTestCase(unittest.TestCase):
if entry.name is None: if entry.name is None:
break break
if entry.name == b'_frozen_importlib': if entry.name in bootstrap_expected:
_fzn_implib_seen = True bootstrap_seen.append(entry.name)
self.assertTrue(entry.size, self.assertTrue(entry.size,
"_frozen_importlib was reported as having no size") "{} was reported as having no size".format(entry.name))
continue continue
items.append((entry.name, entry.size)) items.append((entry.name, entry.size))
...@@ -81,8 +86,8 @@ class Win_ValuesTestCase(unittest.TestCase): ...@@ -81,8 +86,8 @@ class Win_ValuesTestCase(unittest.TestCase):
] ]
self.assertEqual(items, expected) self.assertEqual(items, expected)
self.assertTrue(_fzn_implib_seen, self.assertEqual(sorted(bootstrap_seen), bootstrap_expected,
"_frozen_importlib wasn't found in PyImport_FrozenModules") "frozen bootstrap modules did not match PyImport_FrozenModules")
from ctypes import _pointer_type_cache from ctypes import _pointer_type_cache
del _pointer_type_cache[struct_frozen] del _pointer_type_cache[struct_frozen]
......
...@@ -16,7 +16,8 @@ except ImportError: ...@@ -16,7 +16,8 @@ except ImportError:
# Platform doesn't support dynamic loading. # Platform doesn't support dynamic loading.
load_dynamic = None load_dynamic = None
from importlib._bootstrap import SourcelessFileLoader, _ERR_MSG, _exec, _load from importlib._bootstrap import _ERR_MSG, _exec, _load
from importlib._bootstrap_external import SourcelessFileLoader
from importlib import machinery from importlib import machinery
from importlib import util from importlib import util
......
...@@ -30,9 +30,25 @@ else: ...@@ -30,9 +30,25 @@ else:
pass pass
sys.modules['importlib._bootstrap'] = _bootstrap sys.modules['importlib._bootstrap'] = _bootstrap
try:
import _frozen_importlib_external as _bootstrap_external
except ImportError:
from . import _bootstrap_external
_bootstrap_external._setup(_bootstrap)
else:
_bootstrap_external.__name__ = 'importlib._bootstrap_external'
_bootstrap_external.__package__ = 'importlib'
try:
_bootstrap_external.__file__ = __file__.replace('__init__.py', '_bootstrap_external.py')
except NameError:
# __file__ is not guaranteed to be defined, e.g. if this code gets
# frozen by a tool like cx_Freeze.
pass
sys.modules['importlib._bootstrap_external'] = _bootstrap_external
# To simplify imports in test code # To simplify imports in test code
_w_long = _bootstrap._w_long _w_long = _bootstrap_external._w_long
_r_long = _bootstrap._r_long _r_long = _bootstrap_external._r_long
# Fully bootstrapped at this point, import whatever you like, circular # Fully bootstrapped at this point, import whatever you like, circular
# dependencies and startup overhead minimisation permitting :) # dependencies and startup overhead minimisation permitting :)
......
This diff is collapsed.
This diff is collapsed.
"""Abstract base classes related to import.""" """Abstract base classes related to import."""
from . import _bootstrap from . import _bootstrap_external
from . import machinery from . import machinery
try: try:
import _frozen_importlib import _frozen_importlib
# import _frozen_importlib_external
except ImportError as exc: except ImportError as exc:
if exc.name != '_frozen_importlib': if exc.name != '_frozen_importlib':
raise raise
_frozen_importlib = None _frozen_importlib = None
try:
import _frozen_importlib_external
except ImportError as exc:
_frozen_importlib_external = _bootstrap_external
import abc import abc
...@@ -14,7 +19,10 @@ def _register(abstract_cls, *classes): ...@@ -14,7 +19,10 @@ def _register(abstract_cls, *classes):
for cls in classes: for cls in classes:
abstract_cls.register(cls) abstract_cls.register(cls)
if _frozen_importlib is not None: if _frozen_importlib is not None:
try:
frozen_cls = getattr(_frozen_importlib, cls.__name__) frozen_cls = getattr(_frozen_importlib, cls.__name__)
except AttributeError:
frozen_cls = getattr(_frozen_importlib_external, cls.__name__)
abstract_cls.register(frozen_cls) abstract_cls.register(frozen_cls)
...@@ -102,7 +110,7 @@ class PathEntryFinder(Finder): ...@@ -102,7 +110,7 @@ class PathEntryFinder(Finder):
else: else:
return None, [] return None, []
find_module = _bootstrap._find_module_shim find_module = _bootstrap_external._find_module_shim
def invalidate_caches(self): def invalidate_caches(self):
"""An optional method for clearing the finder's cache, if any. """An optional method for clearing the finder's cache, if any.
...@@ -144,7 +152,7 @@ class Loader(metaclass=abc.ABCMeta): ...@@ -144,7 +152,7 @@ class Loader(metaclass=abc.ABCMeta):
""" """
if not hasattr(self, 'exec_module'): if not hasattr(self, 'exec_module'):
raise ImportError raise ImportError
return _bootstrap._load_module_shim(self, fullname) return _bootstrap_external._load_module_shim(self, fullname)
def module_repr(self, module): def module_repr(self, module):
"""Return a module's repr. """Return a module's repr.
...@@ -222,8 +230,8 @@ class InspectLoader(Loader): ...@@ -222,8 +230,8 @@ class InspectLoader(Loader):
argument should be where the data was retrieved (when applicable).""" argument should be where the data was retrieved (when applicable)."""
return compile(data, path, 'exec', dont_inherit=True) return compile(data, path, 'exec', dont_inherit=True)
exec_module = _bootstrap._LoaderBasics.exec_module exec_module = _bootstrap_external._LoaderBasics.exec_module
load_module = _bootstrap._LoaderBasics.load_module load_module = _bootstrap_external._LoaderBasics.load_module
_register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter) _register(InspectLoader, machinery.BuiltinImporter, machinery.FrozenImporter)
...@@ -265,7 +273,7 @@ class ExecutionLoader(InspectLoader): ...@@ -265,7 +273,7 @@ class ExecutionLoader(InspectLoader):
_register(ExecutionLoader, machinery.ExtensionFileLoader) _register(ExecutionLoader, machinery.ExtensionFileLoader)
class FileLoader(_bootstrap.FileLoader, ResourceLoader, ExecutionLoader): class FileLoader(_bootstrap_external.FileLoader, ResourceLoader, ExecutionLoader):
"""Abstract base class partially implementing the ResourceLoader and """Abstract base class partially implementing the ResourceLoader and
ExecutionLoader ABCs.""" ExecutionLoader ABCs."""
...@@ -274,7 +282,7 @@ _register(FileLoader, machinery.SourceFileLoader, ...@@ -274,7 +282,7 @@ _register(FileLoader, machinery.SourceFileLoader,
machinery.SourcelessFileLoader) machinery.SourcelessFileLoader)
class SourceLoader(_bootstrap.SourceLoader, ResourceLoader, ExecutionLoader): class SourceLoader(_bootstrap_external.SourceLoader, ResourceLoader, ExecutionLoader):
"""Abstract base class for loading source code (and optionally any """Abstract base class for loading source code (and optionally any
corresponding bytecode). corresponding bytecode).
......
...@@ -2,18 +2,18 @@ ...@@ -2,18 +2,18 @@
import _imp import _imp
from ._bootstrap import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
EXTENSION_SUFFIXES)
from ._bootstrap import ModuleSpec from ._bootstrap import ModuleSpec
from ._bootstrap import BuiltinImporter from ._bootstrap import BuiltinImporter
from ._bootstrap import FrozenImporter from ._bootstrap import FrozenImporter
from ._bootstrap import WindowsRegistryFinder from ._bootstrap_external import (SOURCE_SUFFIXES, DEBUG_BYTECODE_SUFFIXES,
from ._bootstrap import PathFinder OPTIMIZED_BYTECODE_SUFFIXES, BYTECODE_SUFFIXES,
from ._bootstrap import FileFinder EXTENSION_SUFFIXES)
from ._bootstrap import SourceFileLoader from ._bootstrap_external import WindowsRegistryFinder
from ._bootstrap import SourcelessFileLoader from ._bootstrap_external import PathFinder
from ._bootstrap import ExtensionFileLoader from ._bootstrap_external import FileFinder
from ._bootstrap_external import SourceFileLoader
from ._bootstrap_external import SourcelessFileLoader
from ._bootstrap_external import ExtensionFileLoader
def all_suffixes(): def all_suffixes():
......
"""Utility code for constructing importers, etc.""" """Utility code for constructing importers, etc."""
from . import abc from . import abc
from ._bootstrap import MAGIC_NUMBER
from ._bootstrap import cache_from_source
from ._bootstrap import decode_source
from ._bootstrap import module_from_spec from ._bootstrap import module_from_spec
from ._bootstrap import source_from_cache
from ._bootstrap import spec_from_loader
from ._bootstrap import spec_from_file_location
from ._bootstrap import _resolve_name from ._bootstrap import _resolve_name
from ._bootstrap import spec_from_loader
from ._bootstrap import _find_spec from ._bootstrap import _find_spec
from ._bootstrap_external import MAGIC_NUMBER
from ._bootstrap_external import cache_from_source
from ._bootstrap_external import decode_source
from ._bootstrap_external import source_from_cache
from ._bootstrap_external import spec_from_file_location
from contextlib import contextmanager from contextlib import contextmanager
import functools import functools
......
"""Find modules used by a script, using introspection.""" """Find modules used by a script, using introspection."""
import dis import dis
import importlib._bootstrap import importlib._bootstrap_external
import importlib.machinery import importlib.machinery
import marshal import marshal
import os import os
...@@ -289,7 +289,7 @@ class ModuleFinder: ...@@ -289,7 +289,7 @@ class ModuleFinder:
co = compile(fp.read()+'\n', pathname, 'exec') co = compile(fp.read()+'\n', pathname, 'exec')
elif type == imp.PY_COMPILED: elif type == imp.PY_COMPILED:
try: try:
marshal_data = importlib._bootstrap._validate_bytecode_header(fp.read()) marshal_data = importlib._bootstrap_external._validate_bytecode_header(fp.read())
except ImportError as exc: except ImportError as exc:
self.msgout(2, "raise ImportError: " + str(exc), pathname) self.msgout(2, "raise ImportError: " + str(exc), pathname)
raise raise
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
This module has intimate knowledge of the format of .pyc files. This module has intimate knowledge of the format of .pyc files.
""" """
import importlib._bootstrap import importlib._bootstrap_external
import importlib.machinery import importlib.machinery
import importlib.util import importlib.util
import os import os
...@@ -137,10 +137,10 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1): ...@@ -137,10 +137,10 @@ def compile(file, cfile=None, dfile=None, doraise=False, optimize=-1):
except FileExistsError: except FileExistsError:
pass pass
source_stats = loader.path_stats(file) source_stats = loader.path_stats(file)
bytecode = importlib._bootstrap._code_to_bytecode( bytecode = importlib._bootstrap_external._code_to_bytecode(
code, source_stats['mtime'], source_stats['size']) code, source_stats['mtime'], source_stats['size'])
mode = importlib._bootstrap._calc_mode(file) mode = importlib._bootstrap_external._calc_mode(file)
importlib._bootstrap._write_atomic(cfile, bytecode, mode) importlib._bootstrap_external._write_atomic(cfile, bytecode, mode)
return cfile return cfile
......
...@@ -53,6 +53,7 @@ Richard Chamberlain, for the first implementation of textdoc. ...@@ -53,6 +53,7 @@ Richard Chamberlain, for the first implementation of textdoc.
import builtins import builtins
import importlib._bootstrap import importlib._bootstrap
import importlib._bootstrap_external
import importlib.machinery import importlib.machinery
import importlib.util import importlib.util
import inspect import inspect
...@@ -292,9 +293,9 @@ def importfile(path): ...@@ -292,9 +293,9 @@ def importfile(path):
filename = os.path.basename(path) filename = os.path.basename(path)
name, ext = os.path.splitext(filename) name, ext = os.path.splitext(filename)
if is_bytecode: if is_bytecode:
loader = importlib._bootstrap.SourcelessFileLoader(name, path) loader = importlib._bootstrap_external.SourcelessFileLoader(name, path)
else: else:
loader = importlib._bootstrap.SourceFileLoader(name, path) loader = importlib._bootstrap_external.SourceFileLoader(name, path)
# XXX We probably don't need to pass in the loader here. # XXX We probably don't need to pass in the loader here.
spec = importlib.util.spec_from_file_location(name, path, loader=loader) spec = importlib.util.spec_from_file_location(name, path, loader=loader)
try: try:
......
...@@ -58,7 +58,7 @@ class _ModifiedArgv0(object): ...@@ -58,7 +58,7 @@ class _ModifiedArgv0(object):
self.value = self._sentinel self.value = self._sentinel
sys.argv[0] = self._saved_value sys.argv[0] = self._saved_value
# TODO: Replace these helpers with importlib._bootstrap functions # TODO: Replace these helpers with importlib._bootstrap_external functions.
def _run_code(code, run_globals, init_globals=None, def _run_code(code, run_globals, init_globals=None,
mod_name=None, mod_spec=None, mod_name=None, mod_spec=None,
pkg_name=None, script_name=None): pkg_name=None, script_name=None):
......
...@@ -98,8 +98,8 @@ def makepath(*paths): ...@@ -98,8 +98,8 @@ def makepath(*paths):
def abs_paths(): def abs_paths():
"""Set all module __file__ and __cached__ attributes to an absolute path""" """Set all module __file__ and __cached__ attributes to an absolute path"""
for m in set(sys.modules.values()): for m in set(sys.modules.values()):
if (getattr(getattr(m, '__loader__', None), '__module__', None) != if (getattr(getattr(m, '__loader__', None), '__module__', None) not in
'_frozen_importlib'): ('_frozen_importlib', '_frozen_importlib_external')):
continue # don't mess with a PEP 302-supplied __file__ continue # don't mess with a PEP 302-supplied __file__
try: try:
m.__file__ = os.path.abspath(m.__file__) m.__file__ = os.path.abspath(m.__file__)
......
# We import importlib *ASAP* in order to test #15386 # We import importlib *ASAP* in order to test #15386
import importlib import importlib
import importlib.util import importlib.util
from importlib._bootstrap import _get_sourcefile from importlib._bootstrap_external import _get_sourcefile
import builtins import builtins
import marshal import marshal
import os import os
...@@ -845,19 +845,27 @@ class ImportlibBootstrapTests(unittest.TestCase): ...@@ -845,19 +845,27 @@ class ImportlibBootstrapTests(unittest.TestCase):
self.assertEqual(mod.__package__, 'importlib') self.assertEqual(mod.__package__, 'importlib')
self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__) self.assertTrue(mod.__file__.endswith('_bootstrap.py'), mod.__file__)
def test_frozen_importlib_external_is_bootstrap_external(self):
from importlib import _bootstrap_external
mod = sys.modules['_frozen_importlib_external']
self.assertIs(mod, _bootstrap_external)
self.assertEqual(mod.__name__, 'importlib._bootstrap_external')
self.assertEqual(mod.__package__, 'importlib')
self.assertTrue(mod.__file__.endswith('_bootstrap_external.py'), mod.__file__)
def test_there_can_be_only_one(self): def test_there_can_be_only_one(self):
# Issue #15386 revealed a tricky loophole in the bootstrapping # Issue #15386 revealed a tricky loophole in the bootstrapping
# This test is technically redundant, since the bug caused importing # This test is technically redundant, since the bug caused importing
# this test module to crash completely, but it helps prove the point # this test module to crash completely, but it helps prove the point
from importlib import machinery from importlib import machinery
mod = sys.modules['_frozen_importlib'] mod = sys.modules['_frozen_importlib']
self.assertIs(machinery.FileFinder, mod.FileFinder) self.assertIs(machinery.ModuleSpec, mod.ModuleSpec)
@cpython_only @cpython_only
class GetSourcefileTests(unittest.TestCase): class GetSourcefileTests(unittest.TestCase):
"""Test importlib._bootstrap._get_sourcefile() as used by the C API. """Test importlib._bootstrap_external._get_sourcefile() as used by the C API.
Because of the peculiarities of the need of this function, the tests are Because of the peculiarities of the need of this function, the tests are
knowingly whitebox tests. knowingly whitebox tests.
...@@ -867,7 +875,7 @@ class GetSourcefileTests(unittest.TestCase): ...@@ -867,7 +875,7 @@ class GetSourcefileTests(unittest.TestCase):
def test_get_sourcefile(self): def test_get_sourcefile(self):
# Given a valid bytecode path, return the path to the corresponding # Given a valid bytecode path, return the path to the corresponding
# source file if it exists. # source file if it exists.
with mock.patch('importlib._bootstrap._path_isfile') as _path_isfile: with mock.patch('importlib._bootstrap_external._path_isfile') as _path_isfile:
_path_isfile.return_value = True; _path_isfile.return_value = True;
path = TESTFN + '.pyc' path = TESTFN + '.pyc'
expect = TESTFN + '.py' expect = TESTFN + '.py'
...@@ -876,7 +884,7 @@ class GetSourcefileTests(unittest.TestCase): ...@@ -876,7 +884,7 @@ class GetSourcefileTests(unittest.TestCase):
def test_get_sourcefile_no_source(self): def test_get_sourcefile_no_source(self):
# Given a valid bytecode path without a corresponding source path, # Given a valid bytecode path without a corresponding source path,
# return the original bytecode path. # return the original bytecode path.
with mock.patch('importlib._bootstrap._path_isfile') as _path_isfile: with mock.patch('importlib._bootstrap_external._path_isfile') as _path_isfile:
_path_isfile.return_value = False; _path_isfile.return_value = False;
path = TESTFN + '.pyc' path = TESTFN + '.pyc'
self.assertEqual(_get_sourcefile(path), path) self.assertEqual(_get_sourcefile(path), path)
...@@ -1031,7 +1039,7 @@ class ImportTracebackTests(unittest.TestCase): ...@@ -1031,7 +1039,7 @@ class ImportTracebackTests(unittest.TestCase):
# We simulate a bug in importlib and check that it's not stripped # We simulate a bug in importlib and check that it's not stripped
# away from the traceback. # away from the traceback.
self.create_module("foo", "") self.create_module("foo", "")
importlib = sys.modules['_frozen_importlib'] importlib = sys.modules['_frozen_importlib_external']
if 'load_module' in vars(importlib.SourceLoader): if 'load_module' in vars(importlib.SourceLoader):
old_exec_module = importlib.SourceLoader.exec_module old_exec_module = importlib.SourceLoader.exec_module
else: else:
......
from importlib import _bootstrap from importlib import _bootstrap_external
import sys import sys
from test import support from test import support
import unittest import unittest
...@@ -26,7 +26,7 @@ class ExtensionModuleCaseSensitivityTest: ...@@ -26,7 +26,7 @@ class ExtensionModuleCaseSensitivityTest:
def test_case_sensitive(self): def test_case_sensitive(self):
with support.EnvironmentVarGuard() as env: with support.EnvironmentVarGuard() as env:
env.unset('PYTHONCASEOK') env.unset('PYTHONCASEOK')
if b'PYTHONCASEOK' in _bootstrap._os.environ: if b'PYTHONCASEOK' in _bootstrap_external._os.environ:
self.skipTest('os.environ changes not reflected in ' self.skipTest('os.environ changes not reflected in '
'_os.environ') '_os.environ')
loader = self.find_module() loader = self.find_module()
...@@ -35,7 +35,7 @@ class ExtensionModuleCaseSensitivityTest: ...@@ -35,7 +35,7 @@ class ExtensionModuleCaseSensitivityTest:
def test_case_insensitivity(self): def test_case_insensitivity(self):
with support.EnvironmentVarGuard() as env: with support.EnvironmentVarGuard() as env:
env.set('PYTHONCASEOK', '1') env.set('PYTHONCASEOK', '1')
if b'PYTHONCASEOK' not in _bootstrap._os.environ: if b'PYTHONCASEOK' not in _bootstrap_external._os.environ:
self.skipTest('os.environ changes not reflected in ' self.skipTest('os.environ changes not reflected in '
'_os.environ') '_os.environ')
loader = self.find_module() loader = self.find_module()
......
...@@ -99,7 +99,7 @@ class FinderTests: ...@@ -99,7 +99,7 @@ class FinderTests:
new_path_importer_cache.pop(None, None) new_path_importer_cache.pop(None, None)
new_path_hooks = [zipimport.zipimporter, new_path_hooks = [zipimport.zipimporter,
self.machinery.FileFinder.path_hook( self.machinery.FileFinder.path_hook(
*self.importlib._bootstrap._get_supported_file_loaders())] *self.importlib._bootstrap_external._get_supported_file_loaders())]
missing = object() missing = object()
email = sys.modules.pop('email', missing) email = sys.modules.pop('email', missing)
try: try:
......
...@@ -42,7 +42,7 @@ class CaseSensitivityTest: ...@@ -42,7 +42,7 @@ class CaseSensitivityTest:
def test_sensitive(self): def test_sensitive(self):
with test_support.EnvironmentVarGuard() as env: with test_support.EnvironmentVarGuard() as env:
env.unset('PYTHONCASEOK') env.unset('PYTHONCASEOK')
if b'PYTHONCASEOK' in self.importlib._bootstrap._os.environ: if b'PYTHONCASEOK' in self.importlib._bootstrap_external._os.environ:
self.skipTest('os.environ changes not reflected in ' self.skipTest('os.environ changes not reflected in '
'_os.environ') '_os.environ')
sensitive, insensitive = self.sensitivity_test() sensitive, insensitive = self.sensitivity_test()
...@@ -53,7 +53,7 @@ class CaseSensitivityTest: ...@@ -53,7 +53,7 @@ class CaseSensitivityTest:
def test_insensitive(self): def test_insensitive(self):
with test_support.EnvironmentVarGuard() as env: with test_support.EnvironmentVarGuard() as env:
env.set('PYTHONCASEOK', '1') env.set('PYTHONCASEOK', '1')
if b'PYTHONCASEOK' not in self.importlib._bootstrap._os.environ: if b'PYTHONCASEOK' not in self.importlib._bootstrap_external._os.environ:
self.skipTest('os.environ changes not reflected in ' self.skipTest('os.environ changes not reflected in '
'_os.environ') '_os.environ')
sensitive, insensitive = self.sensitivity_test() sensitive, insensitive = self.sensitivity_test()
......
...@@ -355,8 +355,10 @@ class ImportSideEffectTests(unittest.TestCase): ...@@ -355,8 +355,10 @@ class ImportSideEffectTests(unittest.TestCase):
stdout, stderr = proc.communicate() stdout, stderr = proc.communicate()
self.assertEqual(proc.returncode, 0) self.assertEqual(proc.returncode, 0)
os__file__, os__cached__ = stdout.splitlines()[:2] os__file__, os__cached__ = stdout.splitlines()[:2]
self.assertTrue(os.path.isabs(os__file__)) self.assertTrue(os.path.isabs(os__file__),
self.assertTrue(os.path.isabs(os__cached__)) "expected absolute path, got {}".format(os__file__))
self.assertTrue(os.path.isabs(os__cached__),
"expected absolute path, got {}".format(os__cached__))
def test_no_duplicate_paths(self): def test_no_duplicate_paths(self):
# No duplicate paths should exist in sys.path # No duplicate paths should exist in sys.path
......
...@@ -533,6 +533,7 @@ coverage-report: ...@@ -533,6 +533,7 @@ coverage-report:
: # force rebuilding of parser and importlib : # force rebuilding of parser and importlib
@touch $(GRAMMAR_INPUT) @touch $(GRAMMAR_INPUT)
@touch $(srcdir)/Lib/importlib/_bootstrap.py @touch $(srcdir)/Lib/importlib/_bootstrap.py
@touch $(srcdir)/Lib/importlib/_bootstrap_external.py
: # build with coverage info : # build with coverage info
$(MAKE) coverage $(MAKE) coverage
: # run tests, ignore failures : # run tests, ignore failures
...@@ -694,6 +695,10 @@ Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile ...@@ -694,6 +695,10 @@ Programs/_freeze_importlib.o: Programs/_freeze_importlib.c Makefile
Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) Programs/_freeze_importlib: Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN)
$(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) $(LINKCC) $(PY_LDFLAGS) -o $@ Programs/_freeze_importlib.o $(LIBRARY_OBJS_OMIT_FROZEN) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST)
Python/importlib_external.h: $(srcdir)/Lib/importlib/_bootstrap_external.py Programs/_freeze_importlib
./Programs/_freeze_importlib \
$(srcdir)/Lib/importlib/_bootstrap_external.py Python/importlib_external.h
Python/importlib.h: $(srcdir)/Lib/importlib/_bootstrap.py Programs/_freeze_importlib Python/importlib.h: $(srcdir)/Lib/importlib/_bootstrap.py Programs/_freeze_importlib
./Programs/_freeze_importlib \ ./Programs/_freeze_importlib \
$(srcdir)/Lib/importlib/_bootstrap.py Python/importlib.h $(srcdir)/Lib/importlib/_bootstrap.py Python/importlib.h
...@@ -841,7 +846,7 @@ $(OPCODETARGETS_H): $(OPCODETARGETGEN_FILES) ...@@ -841,7 +846,7 @@ $(OPCODETARGETS_H): $(OPCODETARGETGEN_FILES)
Python/ceval.o: $(OPCODETARGETS_H) $(srcdir)/Python/ceval_gil.h Python/ceval.o: $(OPCODETARGETS_H) $(srcdir)/Python/ceval_gil.h
Python/frozen.o: Python/importlib.h Python/frozen.o: Python/importlib.h Python/importlib_external.h
Objects/typeobject.o: Objects/typeslots.inc Objects/typeobject.o: Objects/typeslots.inc
Objects/typeslots.inc: $(srcdir)/Include/typeslots.h $(srcdir)/Objects/typeslots.py Objects/typeslots.inc: $(srcdir)/Include/typeslots.h $(srcdir)/Objects/typeslots.py
......
...@@ -15,6 +15,9 @@ Core and Builtins ...@@ -15,6 +15,9 @@ Core and Builtins
- Issue #23910: Optimize property() getter calls. Patch by Joe Jevnik. - Issue #23910: Optimize property() getter calls. Patch by Joe Jevnik.
- Issue #23911: Move path-based importlib bootstrap code to a separate
frozen module.
- Issue #24022: Fix tokenizer crash when processing undecodable source code. - Issue #24022: Fix tokenizer crash when processing undecodable source code.
- Issue #9951: Added a hex() method to bytes, bytearray, and memoryview. - Issue #9951: Added a hex() method to bytes, bytearray, and memoryview.
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
int Py_FrozenFlag = 1; /* Suppress errors from getpath.c */
/* To avoid a circular dependency on frozen.o, we create our own structure /* To avoid a circular dependency on frozen.o, we create our own structure
of frozen modules instead, left deliberately blank so as to avoid of frozen modules instead, left deliberately blank so as to avoid
...@@ -33,13 +34,14 @@ const char header[] = "/* Auto-generated by Programs/_freeze_importlib.c */"; ...@@ -33,13 +34,14 @@ const char header[] = "/* Auto-generated by Programs/_freeze_importlib.c */";
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
char *inpath, *outpath; char *inpath, *outpath, *code_name;
FILE *infile = NULL, *outfile = NULL; FILE *infile = NULL, *outfile = NULL;
struct _Py_stat_struct status; struct _Py_stat_struct status;
size_t text_size, data_size, n; size_t text_size, data_size, n;
char *text = NULL; char *text = NULL;
unsigned char *data; unsigned char *data;
PyObject *code = NULL, *marshalled = NULL; PyObject *code = NULL, *marshalled = NULL;
int is_bootstrap = 1;
PyImport_FrozenModules = _PyImport_FrozenModules; PyImport_FrozenModules = _PyImport_FrozenModules;
...@@ -82,8 +84,14 @@ main(int argc, char *argv[]) ...@@ -82,8 +84,14 @@ main(int argc, char *argv[])
/* Don't install importlib, since it could execute outdated bytecode. */ /* Don't install importlib, since it could execute outdated bytecode. */
_Py_InitializeEx_Private(1, 0); _Py_InitializeEx_Private(1, 0);
code = Py_CompileStringExFlags(text, "<frozen importlib._bootstrap>", if (strstr(inpath, "_external") != NULL) {
Py_file_input, NULL, 0); is_bootstrap = 0;
}
code_name = is_bootstrap ?
"<frozen importlib._bootstrap>" :
"<frozen importlib._bootstrap_external>";
code = Py_CompileStringExFlags(text, code_name, Py_file_input, NULL, 0);
if (code == NULL) if (code == NULL)
goto error; goto error;
free(text); free(text);
...@@ -106,7 +114,11 @@ main(int argc, char *argv[]) ...@@ -106,7 +114,11 @@ main(int argc, char *argv[])
goto error; goto error;
} }
fprintf(outfile, "%s\n", header); fprintf(outfile, "%s\n", header);
if (is_bootstrap)
fprintf(outfile, "const unsigned char _Py_M__importlib[] = {\n"); fprintf(outfile, "const unsigned char _Py_M__importlib[] = {\n");
else
fprintf(outfile,
"const unsigned char _Py_M__importlib_external[] = {\n");
for (n = 0; n < data_size; n += 16) { for (n = 0; n < data_size; n += 16) {
size_t i, end = Py_MIN(n + 16, data_size); size_t i, end = Py_MIN(n + 16, data_size);
fprintf(outfile, " "); fprintf(outfile, " ");
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include "Python.h" #include "Python.h"
#include "importlib.h" #include "importlib.h"
#include "importlib_external.h"
/* In order to test the support for frozen modules, by default we /* In order to test the support for frozen modules, by default we
define a single frozen module, __hello__. Loading it will print define a single frozen module, __hello__. Loading it will print
...@@ -31,6 +32,8 @@ static unsigned char M___hello__[] = { ...@@ -31,6 +32,8 @@ static unsigned char M___hello__[] = {
static const struct _frozen _PyImport_FrozenModules[] = { static const struct _frozen _PyImport_FrozenModules[] = {
/* importlib */ /* importlib */
{"_frozen_importlib", _Py_M__importlib, (int)sizeof(_Py_M__importlib)}, {"_frozen_importlib", _Py_M__importlib, (int)sizeof(_Py_M__importlib)},
{"_frozen_importlib_external", _Py_M__importlib_external,
(int)sizeof(_Py_M__importlib_external)},
/* Test module */ /* Test module */
{"__hello__", M___hello__, SIZE}, {"__hello__", M___hello__, SIZE},
/* Test package (negative size indicates package-ness) */ /* Test package (negative size indicates package-ness) */
......
...@@ -491,8 +491,13 @@ PyImport_GetMagicNumber(void) ...@@ -491,8 +491,13 @@ PyImport_GetMagicNumber(void)
{ {
long res; long res;
PyInterpreterState *interp = PyThreadState_Get()->interp; PyInterpreterState *interp = PyThreadState_Get()->interp;
PyObject *pyc_magic = PyObject_GetAttrString(interp->importlib, PyObject *external, *pyc_magic;
"_RAW_MAGIC_NUMBER");
external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external");
if (external == NULL)
return -1;
pyc_magic = PyObject_GetAttrString(external, "_RAW_MAGIC_NUMBER");
Py_DECREF(external);
if (pyc_magic == NULL) if (pyc_magic == NULL)
return -1; return -1;
res = PyLong_AsLong(pyc_magic); res = PyLong_AsLong(pyc_magic);
...@@ -737,7 +742,7 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, ...@@ -737,7 +742,7 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co,
const char *cpathname) const char *cpathname)
{ {
PyObject *m = NULL; PyObject *m = NULL;
PyObject *nameobj, *pathobj = NULL, *cpathobj = NULL; PyObject *nameobj, *pathobj = NULL, *cpathobj = NULL, *external= NULL;
nameobj = PyUnicode_FromString(name); nameobj = PyUnicode_FromString(name);
if (nameobj == NULL) if (nameobj == NULL)
...@@ -765,9 +770,14 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co, ...@@ -765,9 +770,14 @@ PyImport_ExecCodeModuleWithPathnames(const char *name, PyObject *co,
"no interpreter!"); "no interpreter!");
} }
pathobj = _PyObject_CallMethodIdObjArgs(interp->importlib, external= PyObject_GetAttrString(interp->importlib,
"_bootstrap_external");
if (external != NULL) {
pathobj = _PyObject_CallMethodIdObjArgs(external,
&PyId__get_sourcefile, cpathobj, &PyId__get_sourcefile, cpathobj,
NULL); NULL);
Py_DECREF(external);
}
if (pathobj == NULL) if (pathobj == NULL)
PyErr_Clear(); PyErr_Clear();
} }
...@@ -833,7 +843,7 @@ PyObject* ...@@ -833,7 +843,7 @@ PyObject*
PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname,
PyObject *cpathname) PyObject *cpathname)
{ {
PyObject *d, *res; PyObject *d, *external, *res;
PyInterpreterState *interp = PyThreadState_GET()->interp; PyInterpreterState *interp = PyThreadState_GET()->interp;
_Py_IDENTIFIER(_fix_up_module); _Py_IDENTIFIER(_fix_up_module);
...@@ -845,9 +855,13 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname, ...@@ -845,9 +855,13 @@ PyImport_ExecCodeModuleObject(PyObject *name, PyObject *co, PyObject *pathname,
if (pathname == NULL) { if (pathname == NULL) {
pathname = ((PyCodeObject *)co)->co_filename; pathname = ((PyCodeObject *)co)->co_filename;
} }
res = _PyObject_CallMethodIdObjArgs(interp->importlib, external = PyObject_GetAttrString(interp->importlib, "_bootstrap_external");
if (external == NULL)
return NULL;
res = _PyObject_CallMethodIdObjArgs(external,
&PyId__fix_up_module, &PyId__fix_up_module,
d, name, pathname, cpathname, NULL); d, name, pathname, cpathname, NULL);
Py_DECREF(external);
if (res != NULL) { if (res != NULL) {
Py_DECREF(res); Py_DECREF(res);
res = exec_code_in_module(name, d, co); res = exec_code_in_module(name, d, co);
...@@ -1245,6 +1259,7 @@ static void ...@@ -1245,6 +1259,7 @@ static void
remove_importlib_frames(void) remove_importlib_frames(void)
{ {
const char *importlib_filename = "<frozen importlib._bootstrap>"; const char *importlib_filename = "<frozen importlib._bootstrap>";
const char *external_filename = "<frozen importlib._bootstrap_external>";
const char *remove_frames = "_call_with_frames_removed"; const char *remove_frames = "_call_with_frames_removed";
int always_trim = 0; int always_trim = 0;
int in_importlib = 0; int in_importlib = 0;
...@@ -1274,7 +1289,10 @@ remove_importlib_frames(void) ...@@ -1274,7 +1289,10 @@ remove_importlib_frames(void)
assert(PyTraceBack_Check(tb)); assert(PyTraceBack_Check(tb));
now_in_importlib = (PyUnicode_CompareWithASCIIString( now_in_importlib = (PyUnicode_CompareWithASCIIString(
code->co_filename, code->co_filename,
importlib_filename) == 0); importlib_filename) == 0) ||
(PyUnicode_CompareWithASCIIString(
code->co_filename,
external_filename) == 0);
if (now_in_importlib && !in_importlib) { if (now_in_importlib && !in_importlib) {
/* This is the link to this chunk of importlib tracebacks */ /* This is the link to this chunk of importlib tracebacks */
outer_link = prev_link; outer_link = prev_link;
......
This diff is collapsed.
This diff is collapsed.
...@@ -304,7 +304,7 @@ set_main_loader(PyObject *d, const char *filename, const char *loader_name) ...@@ -304,7 +304,7 @@ set_main_loader(PyObject *d, const char *filename, const char *loader_name)
{ {
PyInterpreterState *interp; PyInterpreterState *interp;
PyThreadState *tstate; PyThreadState *tstate;
PyObject *filename_obj, *loader_type, *loader; PyObject *filename_obj, *bootstrap, *loader_type = NULL, *loader;
int result = 0; int result = 0;
filename_obj = PyUnicode_DecodeFSDefault(filename); filename_obj = PyUnicode_DecodeFSDefault(filename);
...@@ -313,7 +313,12 @@ set_main_loader(PyObject *d, const char *filename, const char *loader_name) ...@@ -313,7 +313,12 @@ set_main_loader(PyObject *d, const char *filename, const char *loader_name)
/* Get current thread state and interpreter pointer */ /* Get current thread state and interpreter pointer */
tstate = PyThreadState_GET(); tstate = PyThreadState_GET();
interp = tstate->interp; interp = tstate->interp;
loader_type = PyObject_GetAttrString(interp->importlib, loader_name); bootstrap = PyObject_GetAttrString(interp->importlib,
"_bootstrap_external");
if (bootstrap != NULL) {
loader_type = PyObject_GetAttrString(bootstrap, loader_name);
Py_DECREF(bootstrap);
}
if (loader_type == NULL) { if (loader_type == NULL) {
Py_DECREF(filename_obj); Py_DECREF(filename_obj);
return -1; return -1;
......
...@@ -366,8 +366,10 @@ def main(): ...@@ -366,8 +366,10 @@ def main():
mf.load_file(mod) mf.load_file(mod)
# Alias "importlib._bootstrap" to "_frozen_importlib" so that the # Alias "importlib._bootstrap" to "_frozen_importlib" so that the
# import machinery can bootstrap. # import machinery can bootstrap. Do the same for
# importlib._bootstrap_external.
mf.modules["_frozen_importlib"] = mf.modules["importlib._bootstrap"] mf.modules["_frozen_importlib"] = mf.modules["importlib._bootstrap"]
mf.modules["_frozen_importlib_external"] = mf.modules["importlib._bootstrap_external"]
# Add the main script as either __main__, or the actual module name. # Add the main script as either __main__, or the actual module name.
if python_entry_is_main: if python_entry_is_main:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment