Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit efad00d

Browse files
committed
Issue #14646: __import__() now sets __loader__ if need be.
importlib.util.module_for_loader also will set __loader__ along with __package__. This is in conjunction to a forthcoming update to PEP 302 which will make these two attributes required for loaders to set.
1 parent fea73ef commit efad00d

5 files changed

Lines changed: 2518 additions & 2408 deletions

File tree

Doc/library/importlib.rst

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -697,22 +697,30 @@ an :term:`importer`.
697697
signature taking two positional arguments
698698
(e.g. ``load_module(self, module)``) for which the second argument
699699
will be the module **object** to be used by the loader.
700-
Note that the decorator
701-
will not work on static methods because of the assumption of two
702-
arguments.
700+
Note that the decorator will not work on static methods because of the
701+
assumption of two arguments.
703702

704703
The decorated method will take in the **name** of the module to be loaded
705704
as expected for a :term:`loader`. If the module is not found in
706705
:data:`sys.modules` then a new one is constructed with its
707-
:attr:`__name__` attribute set. Otherwise the module found in
708-
:data:`sys.modules` will be passed into the method. If an
709-
exception is raised by the decorated method and a module was added to
706+
:attr:`__name__` attribute set to **name**, :attr:`__loader__` set to
707+
**self**, and :attr:`__package__` set if
708+
:meth:`importlib.abc.InspectLoader.is_package` is defined for **self** and
709+
does not raise :exc:`ImportError` for **name**. If a new module is not
710+
needed then the module found in :data:`sys.modules` will be passed into the
711+
method.
712+
713+
If an exception is raised by the decorated method and a module was added to
710714
:data:`sys.modules` it will be removed to prevent a partially initialized
711715
module from being in left in :data:`sys.modules`. If the module was already
712716
in :data:`sys.modules` then it is left alone.
713717

714718
Use of this decorator handles all the details of which module object a
715-
loader should initialize as specified by :pep:`302`.
719+
loader should initialize as specified by :pep:`302` as best as possible.
720+
721+
.. versionchanged:: 3.3
722+
:attr:`__loader__` and :attr:`__package__` are automatically set
723+
(when possible).
716724

717725
.. decorator:: set_loader
718726

@@ -722,6 +730,12 @@ an :term:`importer`.
722730
does nothing. It is assumed that the first positional argument to the
723731
wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set to.
724732

733+
.. note::
734+
735+
It is recommended that :func:`module_for_loader` be used over this
736+
decorator as it subsumes this functionality.
737+
738+
725739
.. decorator:: set_package
726740

727741
A :term:`decorator` for a :term:`loader` to set the :attr:`__package__`
@@ -736,3 +750,7 @@ an :term:`importer`.
736750
attribute set and thus can be used by global level code during
737751
initialization.
738752

753+
.. note::
754+
755+
It is recommended that :func:`module_for_loader` be used over this
756+
decorator as it subsumes this functionality.

Lib/importlib/_bootstrap.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,9 +257,14 @@ def module_for_loader(fxn):
257257
258258
The decorated function is passed the module to use instead of the module
259259
name. The module passed in to the function is either from sys.modules if
260-
it already exists or is a new module which has __name__ set and is inserted
261-
into sys.modules. If an exception is raised and the decorator created the
262-
module it is subsequently removed from sys.modules.
260+
it already exists or is a new module. If the module is new, then __name__
261+
is set the first argument to the method, __loader__ is set to self, and
262+
__package__ is set accordingly (if self.is_package() is defined) will be set
263+
before it is passed to the decorated function (if self.is_package() does
264+
not work for the module it will be set post-load).
265+
266+
If an exception is raised and the decorator created the module it is
267+
subsequently removed from sys.modules.
263268
264269
The decorator assumes that the decorated function takes the module name as
265270
the second argument.
@@ -274,7 +279,18 @@ def module_for_loader_wrapper(self, fullname, *args, **kwargs):
274279
# infinite loop.
275280
module = _new_module(fullname)
276281
sys.modules[fullname] = module
282+
module.__loader__ = self
283+
try:
284+
is_package = self.is_package(fullname)
285+
except (ImportError, AttributeError):
286+
pass
287+
else:
288+
if is_package:
289+
module.__package__ = fullname
290+
else:
291+
module.__package__ = fullname.rpartition('.')[0]
277292
try:
293+
# If __package__ was not set above, __import__() will do it later.
278294
return fxn(self, module, *args, **kwargs)
279295
except:
280296
if not is_reload:
@@ -1012,6 +1028,12 @@ def _find_and_load(name, import_):
10121028
module.__package__ = module.__package__.rpartition('.')[0]
10131029
except AttributeError:
10141030
pass
1031+
# Set loader if need be.
1032+
if not hasattr(module, '__loader__'):
1033+
try:
1034+
module.__loader__ = loader
1035+
except AttributeError:
1036+
pass
10151037
return module
10161038

10171039

Lib/importlib/test/test_util.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,34 @@ def __bool__(self): return False
7979
given = self.return_module(name)
8080
self.assertTrue(given is module)
8181

82+
def test_attributes_set(self):
83+
# __name__, __loader__, and __package__ should be set (when
84+
# is_package() is defined; undefined implicitly tested elsewhere).
85+
class FakeLoader:
86+
def __init__(self, is_package):
87+
self._pkg = is_package
88+
def is_package(self, name):
89+
return self._pkg
90+
@util.module_for_loader
91+
def load_module(self, module):
92+
return module
93+
94+
name = 'pkg.mod'
95+
with test_util.uncache(name):
96+
loader = FakeLoader(False)
97+
module = loader.load_module(name)
98+
self.assertEqual(module.__name__, name)
99+
self.assertIs(module.__loader__, loader)
100+
self.assertEqual(module.__package__, 'pkg')
101+
102+
name = 'pkg.sub'
103+
with test_util.uncache(name):
104+
loader = FakeLoader(True)
105+
module = loader.load_module(name)
106+
self.assertEqual(module.__name__, name)
107+
self.assertIs(module.__loader__, loader)
108+
self.assertEqual(module.__package__, name)
109+
82110

83111
class SetPackageTests(unittest.TestCase):
84112

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ What's New in Python 3.3.0 Alpha 3?
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #14646: __import__() sets __loader__ if the loader did not.
14+
1315
- Issue #14605: No longer have implicit entries in sys.meta_path. If
1416
sys.meta_path is found to be empty, raise ImportWarning.
1517

@@ -79,6 +81,9 @@ Core and Builtins
7981
Library
8082
-------
8183

84+
- Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
85+
__package__ (when possible).
86+
8287
- Issue #14664: It is now possible to use @unittest.skip{If,Unless} on a
8388
test class that doesn't inherit from TestCase (i.e. a mixin).
8489

0 commit comments

Comments
 (0)