Kaydet (Commit) 2cf03a82 authored tarafından Brett Cannon's avatar Brett Cannon

Implement importlib.util.set_loader: a decorator to automatically set

__loader__ on modules.
üst d43b30b0
...@@ -348,7 +348,15 @@ an :term:`importer`. ...@@ -348,7 +348,15 @@ an :term:`importer`.
loader should initialize as specified by :pep:`302`. loader should initialize as specified by :pep:`302`.
.. function:: set_package(method) .. function:: set_loader(fxn)
A :term:`decorator` for a :term:`loader` to set the :attr:`__loader__`
attribute on loaded modules. If the attribute is already set the decorator
does nothing. It is assumed that the first positional argument to the
wrapped method is what :attr:`__loader__` should be set to.
.. function:: set_package(fxn)
A :term:`decorator` for a :term:`loader` to set the :attr:`__package__` A :term:`decorator` for a :term:`loader` to set the :attr:`__package__`
attribute on the module returned by the loader. If :attr:`__package__` is attribute on the module returned by the loader. If :attr:`__package__` is
......
to do to do
///// /////
* Public API left to expose (w/ docs!)
+ util.set_loader
* Implement InspectLoader for BuiltinImporter and FrozenImporter. * Implement InspectLoader for BuiltinImporter and FrozenImporter.
+ Expose function to see if a frozen module is a package. + Expose function to see if a frozen module is a package.
......
...@@ -110,6 +110,17 @@ def set_package(fxn): ...@@ -110,6 +110,17 @@ def set_package(fxn):
return wrapper return wrapper
def set_loader(fxn):
"""Set __loader__ on the returned module."""
def wrapper(self, *args, **kwargs):
module = fxn(self, *args, **kwargs)
if not hasattr(module, '__loader__'):
module.__loader__ = self
return module
wrap(wrapper, fxn)
return wrapper
class BuiltinImporter: class BuiltinImporter:
"""Meta path loader for built-in modules. """Meta path loader for built-in modules.
...@@ -132,6 +143,7 @@ class BuiltinImporter: ...@@ -132,6 +143,7 @@ class BuiltinImporter:
@classmethod @classmethod
@set_package @set_package
@set_loader
def load_module(cls, fullname): def load_module(cls, fullname):
"""Load a built-in module.""" """Load a built-in module."""
if fullname not in sys.builtin_module_names: if fullname not in sys.builtin_module_names:
...@@ -161,6 +173,7 @@ class FrozenImporter: ...@@ -161,6 +173,7 @@ class FrozenImporter:
@classmethod @classmethod
@set_package @set_package
@set_loader
def load_module(cls, fullname): def load_module(cls, fullname):
"""Load a frozen module.""" """Load a frozen module."""
if cls.find_module(fullname) is None: if cls.find_module(fullname) is None:
...@@ -249,13 +262,12 @@ class _ExtensionFileLoader: ...@@ -249,13 +262,12 @@ class _ExtensionFileLoader:
@check_name @check_name
@set_package @set_package
@set_loader
def load_module(self, fullname): def load_module(self, fullname):
"""Load an extension module.""" """Load an extension module."""
is_reload = fullname in sys.modules is_reload = fullname in sys.modules
try: try:
module = imp.load_dynamic(fullname, self._path) return imp.load_dynamic(fullname, self._path)
module.__loader__ = self
return module
except: except:
if not is_reload and fullname in sys.modules: if not is_reload and fullname in sys.modules:
del sys.modules[fullname] del sys.modules[fullname]
......
...@@ -15,7 +15,8 @@ class LoaderTests(abc.LoaderTests): ...@@ -15,7 +15,8 @@ class LoaderTests(abc.LoaderTests):
assert 'errno' in sys.builtin_module_names assert 'errno' in sys.builtin_module_names
name = 'errno' name = 'errno'
verification = {'__name__': 'errno', '__package__': ''} verification = {'__name__': 'errno', '__package__': '',
'__loader__': machinery.BuiltinImporter}
def verify(self, module): def verify(self, module):
"""Verify that the module matches against what it should have.""" """Verify that the module matches against what it should have."""
......
...@@ -24,6 +24,8 @@ class LoaderTests(abc.LoaderTests): ...@@ -24,6 +24,8 @@ class LoaderTests(abc.LoaderTests):
('__package__', '')]: ('__package__', '')]:
self.assertEqual(getattr(module, attr), value) self.assertEqual(getattr(module, attr), value)
self.assert_(ext_util.NAME in sys.modules) self.assert_(ext_util.NAME in sys.modules)
self.assert_(isinstance(module.__loader__,
importlib._ExtensionFileLoader))
def test_package(self): def test_package(self):
# Extensions are not found in packages. # Extensions are not found in packages.
......
...@@ -9,7 +9,7 @@ class LoaderTests(abc.LoaderTests): ...@@ -9,7 +9,7 @@ class LoaderTests(abc.LoaderTests):
with util.uncache('__hello__'): with util.uncache('__hello__'):
module = machinery.FrozenImporter.load_module('__hello__') module = machinery.FrozenImporter.load_module('__hello__')
check = {'__name__': '__hello__', '__file__': '<frozen>', check = {'__name__': '__hello__', '__file__': '<frozen>',
'__package__': ''} '__package__': '', '__loader__': machinery.FrozenImporter}
for attr, value in check.items(): for attr, value in check.items():
self.assertEqual(getattr(module, attr), value) self.assertEqual(getattr(module, attr), value)
...@@ -17,7 +17,8 @@ class LoaderTests(abc.LoaderTests): ...@@ -17,7 +17,8 @@ class LoaderTests(abc.LoaderTests):
with util.uncache('__phello__'): with util.uncache('__phello__'):
module = machinery.FrozenImporter.load_module('__phello__') module = machinery.FrozenImporter.load_module('__phello__')
check = {'__name__': '__phello__', '__file__': '<frozen>', check = {'__name__': '__phello__', '__file__': '<frozen>',
'__package__': '__phello__', '__path__': ['__phello__']} '__package__': '__phello__', '__path__': ['__phello__'],
'__loader__': machinery.FrozenImporter}
for attr, value in check.items(): for attr, value in check.items():
attr_value = getattr(module, attr) attr_value = getattr(module, attr)
self.assertEqual(attr_value, value, self.assertEqual(attr_value, value,
...@@ -28,7 +29,8 @@ class LoaderTests(abc.LoaderTests): ...@@ -28,7 +29,8 @@ class LoaderTests(abc.LoaderTests):
with util.uncache('__phello__', '__phello__.spam'): with util.uncache('__phello__', '__phello__.spam'):
module = machinery.FrozenImporter.load_module('__phello__.spam') module = machinery.FrozenImporter.load_module('__phello__.spam')
check = {'__name__': '__phello__.spam', '__file__': '<frozen>', check = {'__name__': '__phello__.spam', '__file__': '<frozen>',
'__package__': '__phello__'} '__package__': '__phello__',
'__loader__': machinery.FrozenImporter}
for attr, value in check.items(): for attr, value in check.items():
attr_value = getattr(module, attr) attr_value = getattr(module, attr)
self.assertEqual(attr_value, value, self.assertEqual(attr_value, value,
......
"""Utility code for constructing importers, etc.""" """Utility code for constructing importers, etc."""
from ._bootstrap import module_for_loader from ._bootstrap import module_for_loader
from ._bootstrap import set_loader
from ._bootstrap import set_package from ._bootstrap import set_package
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