[Python-checkins] cpython: Issue #14646: __import__() now sets __loader__ if need be.

brett.cannon python-checkins at python.org
Fri Apr 27 23:33:07 CEST 2012


http://hg.python.org/cpython/rev/496c68f90a03
changeset:   76586:496c68f90a03
parent:      76584:141ed4b426e1
user:        Brett Cannon <brett at python.org>
date:        Fri Apr 27 17:27:14 2012 -0400
summary:
  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.

files:
  Doc/library/importlib.rst       |   32 +++++++++++++++----
  Lib/importlib/_bootstrap.py     |   28 +++++++++++++++-
  Lib/importlib/test/test_util.py |   28 +++++++++++++++++
  Misc/NEWS                       |    5 +++
  Python/importlib.h              |  Bin 
  5 files changed, 83 insertions(+), 10 deletions(-)


diff --git a/Doc/library/importlib.rst b/Doc/library/importlib.rst
--- a/Doc/library/importlib.rst
+++ b/Doc/library/importlib.rst
@@ -697,22 +697,30 @@
     signature taking two positional arguments
     (e.g. ``load_module(self, module)``) for which the second argument
     will be the module **object** to be used by the loader.
-    Note that the decorator
-    will not work on static methods because of the assumption of two
-    arguments.
+    Note that the decorator will not work on static methods because of the
+    assumption of two arguments.
 
     The decorated method will take in the **name** of the module to be loaded
     as expected for a :term:`loader`. If the module is not found in
     :data:`sys.modules` then a new one is constructed with its
-    :attr:`__name__` attribute set. Otherwise the module found in
-    :data:`sys.modules` will be passed into the method. If an
-    exception is raised by the decorated method and a module was added to
+    :attr:`__name__` attribute set to **name**, :attr:`__loader__` set to
+    **self**, and :attr:`__package__` set if
+    :meth:`importlib.abc.InspectLoader.is_package` is defined for **self** and
+    does not raise :exc:`ImportError` for **name**. If a new module is not
+    needed then the module found in :data:`sys.modules` will be passed into the
+    method.
+
+    If an exception is raised by the decorated method and a module was added to
     :data:`sys.modules` it will be removed to prevent a partially initialized
     module from being in left in :data:`sys.modules`. If the module was already
     in :data:`sys.modules` then it is left alone.
 
     Use of this decorator handles all the details of which module object a
-    loader should initialize as specified by :pep:`302`.
+    loader should initialize as specified by :pep:`302` as best as possible.
+
+    .. versionchanged:: 3.3
+      :attr:`__loader__` and :attr:`__package__` are automatically set
+      (when possible).
 
 .. decorator:: set_loader
 
@@ -722,6 +730,12 @@
     does nothing. It is assumed that the first positional argument to the
     wrapped method (i.e. ``self``) is what :attr:`__loader__` should be set to.
 
+   .. note::
+
+      It is recommended that :func:`module_for_loader` be used over this
+      decorator as it subsumes this functionality.
+
+
 .. decorator:: set_package
 
     A :term:`decorator` for a :term:`loader` to set the :attr:`__package__`
@@ -736,3 +750,7 @@
     attribute set and thus can be used by global level code during
     initialization.
 
+   .. note::
+
+      It is recommended that :func:`module_for_loader` be used over this
+      decorator as it subsumes this functionality.
diff --git a/Lib/importlib/_bootstrap.py b/Lib/importlib/_bootstrap.py
--- a/Lib/importlib/_bootstrap.py
+++ b/Lib/importlib/_bootstrap.py
@@ -257,9 +257,14 @@
 
     The decorated function is passed the module to use instead of the module
     name. The module passed in to the function is either from sys.modules if
-    it already exists or is a new module which has __name__ set and is inserted
-    into sys.modules. If an exception is raised and the decorator created the
-    module it is subsequently removed from sys.modules.
+    it already exists or is a new module. If the module is new, then __name__
+    is set the first argument to the method, __loader__ is set to self, and
+    __package__ is set accordingly (if self.is_package() is defined) will be set
+    before it is passed to the decorated function (if self.is_package() does
+    not work for the module it will be set post-load).
+
+    If an exception is raised and the decorator created the module it is
+    subsequently removed from sys.modules.
 
     The decorator assumes that the decorated function takes the module name as
     the second argument.
@@ -274,7 +279,18 @@
             # infinite loop.
             module = _new_module(fullname)
             sys.modules[fullname] = module
+            module.__loader__ = self
+            try:
+                is_package = self.is_package(fullname)
+            except (ImportError, AttributeError):
+                pass
+            else:
+                if is_package:
+                    module.__package__ = fullname
+                else:
+                    module.__package__ = fullname.rpartition('.')[0]
         try:
+            # If __package__ was not set above, __import__() will do it later.
             return fxn(self, module, *args, **kwargs)
         except:
             if not is_reload:
@@ -1012,6 +1028,12 @@
                 module.__package__ = module.__package__.rpartition('.')[0]
         except AttributeError:
             pass
+    # Set loader if need be.
+    if not hasattr(module, '__loader__'):
+        try:
+            module.__loader__ = loader
+        except AttributeError:
+            pass
     return module
 
 
diff --git a/Lib/importlib/test/test_util.py b/Lib/importlib/test/test_util.py
--- a/Lib/importlib/test/test_util.py
+++ b/Lib/importlib/test/test_util.py
@@ -79,6 +79,34 @@
             given = self.return_module(name)
             self.assertTrue(given is module)
 
+    def test_attributes_set(self):
+        # __name__, __loader__, and __package__ should be set (when
+        # is_package() is defined; undefined implicitly tested elsewhere).
+        class FakeLoader:
+            def __init__(self, is_package):
+                self._pkg = is_package
+            def is_package(self, name):
+                return self._pkg
+            @util.module_for_loader
+            def load_module(self, module):
+                return module
+
+        name = 'pkg.mod'
+        with test_util.uncache(name):
+            loader = FakeLoader(False)
+            module = loader.load_module(name)
+            self.assertEqual(module.__name__, name)
+            self.assertIs(module.__loader__, loader)
+            self.assertEqual(module.__package__, 'pkg')
+
+        name = 'pkg.sub'
+        with test_util.uncache(name):
+            loader = FakeLoader(True)
+            module = loader.load_module(name)
+            self.assertEqual(module.__name__, name)
+            self.assertIs(module.__loader__, loader)
+            self.assertEqual(module.__package__, name)
+
 
 class SetPackageTests(unittest.TestCase):
 
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,8 @@
 Core and Builtins
 -----------------
 
+- Issue #14646: __import__() sets __loader__ if the loader did not.
+
 - Issue #14605: No longer have implicit entries in sys.meta_path. If
   sys.meta_path is found to be empty, raise ImportWarning.
 
@@ -79,6 +81,9 @@
 Library
 -------
 
+- Issue #14646: importlib.util.module_for_loader() now sets __loader__ and
+  __package__ (when possible).
+
 - Issue #14664: It is now possible to use @unittest.skip{If,Unless} on a
   test class that doesn't inherit from TestCase (i.e. a mixin).
 
diff --git a/Python/importlib.h b/Python/importlib.h
index cc3ddc43349236a87b80a231ff6a6b576251f89e..e9f5faf2fa9b7e239563aff1db46d6e1d70fcb88
GIT binary patch
[stripped]

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list