Skip to content

Commit 79c1035

Browse files
Make sure frozen modules are fixed up properly.
1 parent ac5b7c1 commit 79c1035

File tree

2 files changed

+84
-36
lines changed

2 files changed

+84
-36
lines changed

Lib/importlib/_bootstrap.py

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,10 @@ def has_location(self, value):
421421

422422
def spec_from_loader(name, loader, *, origin=None, is_package=None):
423423
"""Return a module spec based on various loader methods."""
424-
if hasattr(loader, 'get_filename'):
424+
if origin is None:
425+
origin = getattr(loader, '_ORIGIN', None)
426+
427+
if not origin and hasattr(loader, 'get_filename'):
425428
if _bootstrap_external is None:
426429
raise NotImplementedError
427430
spec_from_file_location = _bootstrap_external.spec_from_file_location
@@ -467,12 +470,9 @@ def _spec_from_module(module, loader=None, origin=None):
467470
except AttributeError:
468471
location = None
469472
if origin is None:
470-
if location is None:
471-
try:
472-
origin = loader._ORIGIN
473-
except AttributeError:
474-
origin = None
475-
else:
473+
if loader is not None:
474+
origin = getattr(loader, '_ORIGIN', None)
475+
if not origin and location is not None:
476476
origin = location
477477
try:
478478
cached = module.__cached__
@@ -484,7 +484,7 @@ def _spec_from_module(module, loader=None, origin=None):
484484
submodule_search_locations = None
485485

486486
spec = ModuleSpec(name, loader, origin=origin)
487-
spec._set_fileattr = False if location is None else True
487+
spec._set_fileattr = origin == location
488488
spec.cached = cached
489489
spec.submodule_search_locations = submodule_search_locations
490490
return spec
@@ -825,32 +825,67 @@ def module_repr(m):
825825
return '<module {!r} ({})>'.format(m.__name__, FrozenImporter._ORIGIN)
826826

827827
@classmethod
828-
def _setup_module(cls, module):
829-
ispkg = hasattr(module, '__path__')
828+
def _fix_up_module(cls, module):
830829
spec = module.__spec__
831-
assert not ispkg or spec.submodule_search_locations is not None
830+
state = spec.loader_state
831+
if state is None:
832+
# The module is missing FrozenImporter-specific values.
832833

833-
if spec.loader_state is None:
834-
spec.loader_state = type(sys.implementation)(
835-
data=None,
836-
origname=None,
837-
)
838-
elif not hasattr(spec.loader_state, 'data'):
839-
spec.loader_state.data = None
840-
if not getattr(spec.loader_state, 'origname', None):
834+
# Fix up the spec attrs.
841835
origname = vars(module).pop('__origname__', None)
842836
assert origname, 'see PyImport_ImportFrozenModuleObject()'
843-
spec.loader_state.origname = origname
844-
if not getattr(spec.loader_state, 'filename', None):
845-
# Note that this happens early in runtime initialization.
846-
# So sys._stdlib_dir isn't set yet...
847-
filename, pkgdir = cls._resolve_filename(origname, ispkg)
848-
if filename:
849-
module.__file__ = filename
837+
ispkg = hasattr(module, '__path__')
838+
assert _imp.is_frozen_package(module.__name__) == ispkg, ispkg
839+
filename, pkgdir = cls._resolve_filename(origname, spec.name, ispkg)
840+
spec.loader_state = state = type(sys.implementation)(
841+
data=None,
842+
filename=filename,
843+
origname=origname,
844+
)
845+
__path__ = spec.submodule_search_locations
846+
if ispkg:
847+
assert __path__ == [], __path__
850848
if pkgdir:
851849
spec.submodule_search_locations.insert(0, pkgdir)
852-
module.__path__.insert(0, pkgdir)
853-
spec.loader_state.filename = filename or None
850+
else:
851+
assert __path__ is None, __path__
852+
853+
# Fix up the module attrs (the bare minimum).
854+
assert not hasattr(module, '__file__'), module.__file__
855+
if filename:
856+
try:
857+
module.__file__ = filename
858+
except AttributeError:
859+
pass
860+
if ispkg:
861+
if module.__path__ != __path__:
862+
assert not module.__path__, module.__path__
863+
# XXX _init_module_attrs() should copy like this too.
864+
module.__path__.extend(__path__)
865+
else:
866+
# These checks ensure that _fix_up_module() is only called
867+
# in the right places.
868+
assert state is not None
869+
assert sorted(vars(state)) == ['data', 'filename', 'origname'], state
870+
assert state.data is None, state.data
871+
assert state.origname
872+
__path__ = spec.submodule_search_locations
873+
ispkg = __path__ is not None
874+
(filename, pkgdir,
875+
) = cls._resolve_filename(state.origname, spec.name, ispkg)
876+
assert state.filename == filename, (state.filename, filename)
877+
if pkgdir:
878+
assert __path__ == [pkgdir], (__path__, pkgdir)
879+
elif ispkg:
880+
assert __path__ == [], __path__
881+
assert hasattr(module, '__file__')
882+
assert module.__file__ == filename, (module.__file__, filename)
883+
if ispkg:
884+
assert hasattr(module, '__path__')
885+
assert module.__path__ == __path__, (module.__path__, __path__)
886+
else:
887+
assert not hasattr(module, '__path__'), module.__path__
888+
assert not spec.has_location
854889

855890
@classmethod
856891
def _resolve_filename(cls, fullname, alias=None, ispkg=False):
@@ -949,7 +984,16 @@ def load_module(cls, fullname):
949984
950985
"""
951986
# Warning about deprecation implemented in _load_module_shim().
952-
return _load_module_shim(cls, fullname)
987+
module = _load_module_shim(cls, fullname)
988+
info = _imp.find_frozen(fullname)
989+
assert info is not None
990+
_, ispkg, origname = info
991+
module.__origname__ = origname
992+
vars(module).pop('__file__', None)
993+
if ispkg:
994+
module.__path__ = []
995+
cls._fix_up_module(module)
996+
return module
953997

954998
@classmethod
955999
@_requires_frozen
@@ -1290,7 +1334,7 @@ def _setup(sys_module, _imp_module):
12901334
spec = _spec_from_module(module, loader)
12911335
_init_module_attrs(spec, module)
12921336
if loader is FrozenImporter:
1293-
loader._setup_module(module)
1337+
loader._fix_up_module(module)
12941338

12951339
# Directly load built-in modules needed during bootstrap.
12961340
self_module = sys.modules[__name__]

Lib/test/test_importlib/frozen/test_loader.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,44 +149,48 @@ def load_module(self, name):
149149

150150
def test_module(self):
151151
module, stdout = self.load_module('__hello__')
152+
filename = resolve_stdlib_file('__hello__')
152153
check = {'__name__': '__hello__',
153154
'__package__': '',
154155
'__loader__': self.machinery.FrozenImporter,
156+
'__file__': filename,
155157
}
156158
for attr, value in check.items():
157-
self.assertEqual(getattr(module, attr), value)
159+
self.assertEqual(getattr(module, attr, None), value)
158160
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
159-
self.assertFalse(hasattr(module, '__file__'))
160161

161162
def test_package(self):
162163
module, stdout = self.load_module('__phello__')
164+
filename = resolve_stdlib_file('__phello__', ispkg=True)
165+
pkgdir = os.path.dirname(filename)
163166
check = {'__name__': '__phello__',
164167
'__package__': '__phello__',
165-
'__path__': [],
168+
'__path__': [pkgdir],
166169
'__loader__': self.machinery.FrozenImporter,
170+
'__file__': filename,
167171
}
168172
for attr, value in check.items():
169-
attr_value = getattr(module, attr)
173+
attr_value = getattr(module, attr, None)
170174
self.assertEqual(attr_value, value,
171175
"for __phello__.%s, %r != %r" %
172176
(attr, attr_value, value))
173177
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
174-
self.assertFalse(hasattr(module, '__file__'))
175178

176179
def test_lacking_parent(self):
177180
with util.uncache('__phello__'):
178181
module, stdout = self.load_module('__phello__.spam')
182+
filename = resolve_stdlib_file('__phello__.spam')
179183
check = {'__name__': '__phello__.spam',
180184
'__package__': '__phello__',
181185
'__loader__': self.machinery.FrozenImporter,
186+
'__file__': filename,
182187
}
183188
for attr, value in check.items():
184189
attr_value = getattr(module, attr)
185190
self.assertEqual(attr_value, value,
186191
"for __phello__.spam.%s, %r != %r" %
187192
(attr, attr_value, value))
188193
self.assertEqual(stdout.getvalue(), 'Hello world!\n')
189-
self.assertFalse(hasattr(module, '__file__'))
190194

191195
def test_module_reuse(self):
192196
with fresh('__hello__', oldapi=True):

0 commit comments

Comments
 (0)