mirror of https://github.com/python/cpython
Issue #17117: Have both import itself and importlib.util.set_loader()
set __loader__ on a module when set to None. Thanks to Gökcen Eraslan for the fix.
This commit is contained in:
parent
aa73a1c9c9
commit
4802becb16
|
@ -774,6 +774,10 @@ an :term:`importer`.
|
||||||
It is recommended that :func:`module_for_loader` be used over this
|
It is recommended that :func:`module_for_loader` be used over this
|
||||||
decorator as it subsumes this functionality.
|
decorator as it subsumes this functionality.
|
||||||
|
|
||||||
|
.. versionchanged:: 3.4
|
||||||
|
Set ``__loader__`` if set to ``None`` as well if the attribute does not
|
||||||
|
exist.
|
||||||
|
|
||||||
|
|
||||||
.. decorator:: set_package
|
.. decorator:: set_package
|
||||||
|
|
||||||
|
|
|
@ -369,16 +369,18 @@ Loaders must satisfy the following requirements:
|
||||||
* The ``__loader__`` attribute must be set to the loader object that loaded
|
* The ``__loader__`` attribute must be set to the loader object that loaded
|
||||||
the module. This is mostly for introspection and reloading, but can be
|
the module. This is mostly for introspection and reloading, but can be
|
||||||
used for additional loader-specific functionality, for example getting
|
used for additional loader-specific functionality, for example getting
|
||||||
data associated with a loader.
|
data associated with a loader. If the attribute is missing or set to ``None``
|
||||||
|
then the import machinery will automatically set it **after** the module has
|
||||||
|
been imported.
|
||||||
|
|
||||||
* The module's ``__package__`` attribute should be set. Its value must be a
|
* The module's ``__package__`` attribute must be set. Its value must be a
|
||||||
string, but it can be the same value as its ``__name__``. If the attribute
|
string, but it can be the same value as its ``__name__``. If the attribute
|
||||||
is set to ``None`` or is missing, the import system will fill it in with a
|
is set to ``None`` or is missing, the import system will fill it in with a
|
||||||
more appropriate value. When the module is a package, its ``__package__``
|
more appropriate value **after** the module has been imported.
|
||||||
value should be set to its ``__name__``. When the module is not a package,
|
When the module is a package, its ``__package__`` value should be set to its
|
||||||
``__package__`` should be set to the empty string for top-level modules, or
|
``__name__``. When the module is not a package, ``__package__`` should be
|
||||||
for submodules, to the parent package's name. See :pep:`366` for further
|
set to the empty string for top-level modules, or for submodules, to the
|
||||||
details.
|
parent package's name. See :pep:`366` for further details.
|
||||||
|
|
||||||
This attribute is used instead of ``__name__`` to calculate explicit
|
This attribute is used instead of ``__name__`` to calculate explicit
|
||||||
relative imports for main modules, as defined in :pep:`366`.
|
relative imports for main modules, as defined in :pep:`366`.
|
||||||
|
|
|
@ -494,7 +494,7 @@ def set_loader(fxn):
|
||||||
"""Set __loader__ on the returned module."""
|
"""Set __loader__ on the returned module."""
|
||||||
def set_loader_wrapper(self, *args, **kwargs):
|
def set_loader_wrapper(self, *args, **kwargs):
|
||||||
module = fxn(self, *args, **kwargs)
|
module = fxn(self, *args, **kwargs)
|
||||||
if not hasattr(module, '__loader__'):
|
if getattr(module, '__loader__', None) is None:
|
||||||
module.__loader__ = self
|
module.__loader__ = self
|
||||||
return module
|
return module
|
||||||
_wrap(set_loader_wrapper, fxn)
|
_wrap(set_loader_wrapper, fxn)
|
||||||
|
@ -875,12 +875,9 @@ class _LoaderBasics:
|
||||||
module.__cached__ = module.__file__
|
module.__cached__ = module.__file__
|
||||||
else:
|
else:
|
||||||
module.__cached__ = module.__file__
|
module.__cached__ = module.__file__
|
||||||
module.__package__ = name
|
|
||||||
if self.is_package(name):
|
if self.is_package(name):
|
||||||
module.__path__ = [_path_split(module.__file__)[0]]
|
module.__path__ = [_path_split(module.__file__)[0]]
|
||||||
else:
|
# __package__ and __loader set by @module_for_loader.
|
||||||
module.__package__ = module.__package__.rpartition('.')[0]
|
|
||||||
module.__loader__ = self
|
|
||||||
_call_with_frames_removed(exec, code_object, module.__dict__)
|
_call_with_frames_removed(exec, code_object, module.__dict__)
|
||||||
return module
|
return module
|
||||||
|
|
||||||
|
@ -1551,7 +1548,7 @@ def _find_and_load_unlocked(name, import_):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
# Set loader if need be.
|
# Set loader if need be.
|
||||||
if not hasattr(module, '__loader__'):
|
if getattr(module, '__loader__', None) is None:
|
||||||
try:
|
try:
|
||||||
module.__loader__ = loader
|
module.__loader__ = loader
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import imp
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from .. import util
|
||||||
|
from . import util as import_util
|
||||||
|
|
||||||
|
|
||||||
|
class LoaderMock:
|
||||||
|
|
||||||
|
def find_module(self, fullname, path=None):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def load_module(self, fullname):
|
||||||
|
sys.modules[fullname] = self.module
|
||||||
|
return self.module
|
||||||
|
|
||||||
|
|
||||||
|
class LoaderAttributeTests(unittest.TestCase):
|
||||||
|
|
||||||
|
def test___loader___missing(self):
|
||||||
|
module = imp.new_module('blah')
|
||||||
|
try:
|
||||||
|
del module.__loader__
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
loader = LoaderMock()
|
||||||
|
loader.module = module
|
||||||
|
with util.uncache('blah'), util.import_state(meta_path=[loader]):
|
||||||
|
module = import_util.import_('blah')
|
||||||
|
self.assertEqual(loader, module.__loader__)
|
||||||
|
|
||||||
|
def test___loader___is_None(self):
|
||||||
|
module = imp.new_module('blah')
|
||||||
|
module.__loader__ = None
|
||||||
|
loader = LoaderMock()
|
||||||
|
loader.module = module
|
||||||
|
with util.uncache('blah'), util.import_state(meta_path=[loader]):
|
||||||
|
returned_module = import_util.import_('blah')
|
||||||
|
self.assertEqual(loader, module.__loader__)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -162,6 +162,37 @@ class SetPackageTests(unittest.TestCase):
|
||||||
self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
|
self.assertEqual(wrapped.__qualname__, fxn.__qualname__)
|
||||||
|
|
||||||
|
|
||||||
|
class SetLoaderTests(unittest.TestCase):
|
||||||
|
|
||||||
|
"""Tests importlib.util.set_loader()."""
|
||||||
|
|
||||||
|
class DummyLoader:
|
||||||
|
@util.set_loader
|
||||||
|
def load_module(self, module):
|
||||||
|
return self.module
|
||||||
|
|
||||||
|
def test_no_attribute(self):
|
||||||
|
loader = self.DummyLoader()
|
||||||
|
loader.module = imp.new_module('blah')
|
||||||
|
try:
|
||||||
|
del loader.module.__loader__
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
self.assertEqual(loader, loader.load_module('blah').__loader__)
|
||||||
|
|
||||||
|
def test_attribute_is_None(self):
|
||||||
|
loader = self.DummyLoader()
|
||||||
|
loader.module = imp.new_module('blah')
|
||||||
|
loader.module.__loader__ = None
|
||||||
|
self.assertEqual(loader, loader.load_module('blah').__loader__)
|
||||||
|
|
||||||
|
def test_not_reset(self):
|
||||||
|
loader = self.DummyLoader()
|
||||||
|
loader.module = imp.new_module('blah')
|
||||||
|
loader.module.__loader__ = 42
|
||||||
|
self.assertEqual(42, loader.load_module('blah').__loader__)
|
||||||
|
|
||||||
|
|
||||||
class ResolveNameTests(unittest.TestCase):
|
class ResolveNameTests(unittest.TestCase):
|
||||||
|
|
||||||
"""Tests importlib.util.resolve_name()."""
|
"""Tests importlib.util.resolve_name()."""
|
||||||
|
@ -195,14 +226,5 @@ class ResolveNameTests(unittest.TestCase):
|
||||||
util.resolve_name('..bacon', 'spam')
|
util.resolve_name('..bacon', 'spam')
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
|
||||||
from test import support
|
|
||||||
support.run_unittest(
|
|
||||||
ModuleForLoaderTests,
|
|
||||||
SetPackageTests,
|
|
||||||
ResolveNameTests
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
test_main()
|
unittest.main()
|
||||||
|
|
|
@ -339,6 +339,7 @@ David Ely
|
||||||
Jeff Epler
|
Jeff Epler
|
||||||
Jeff McNeil
|
Jeff McNeil
|
||||||
Tom Epperly
|
Tom Epperly
|
||||||
|
Gökcen Eraslan
|
||||||
Stoffel Erasmus
|
Stoffel Erasmus
|
||||||
Jürgen A. Erhard
|
Jürgen A. Erhard
|
||||||
Michael Ernst
|
Michael Ernst
|
||||||
|
|
|
@ -10,6 +10,9 @@ What's New in Python 3.4.0 Alpha 1?
|
||||||
Core and Builtins
|
Core and Builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #17117: Import and @importlib.util.set_loader now set __loader__ when
|
||||||
|
it has a value of None or the attribute doesn't exist.
|
||||||
|
|
||||||
- Issue #17327: Add PyDict_SetDefault.
|
- Issue #17327: Add PyDict_SetDefault.
|
||||||
|
|
||||||
- Issue #17032: The "global" in the "NameError: global name 'x' is not defined"
|
- Issue #17032: The "global" in the "NameError: global name 'x' is not defined"
|
||||||
|
|
6705
Python/importlib.h
6705
Python/importlib.h
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue