[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