Kaydet (Commit) 4802becb authored tarafından Brett Cannon's avatar Brett Cannon

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.
üst aa73a1c9
...@@ -774,6 +774,10 @@ an :term:`importer`. ...@@ -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: ...@@ -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): ...@@ -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: ...@@ -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_): ...@@ -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:
......
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()
\ No newline at end of file
...@@ -162,6 +162,37 @@ class SetPackageTests(unittest.TestCase): ...@@ -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): ...@@ -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 ...@@ -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? ...@@ -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"
......
This diff is collapsed.
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