mirror of https://github.com/python/cpython
Issue #13959: Introduce importlib.find_loader().
The long-term goal is to deprecate imp.find_module() in favour of this API, but it will take some time as some APIs explicitly return/use what imp.find_module() returns.
This commit is contained in:
parent
acc0c181a8
commit
ee78a2b51c
|
@ -86,6 +86,20 @@ Functions
|
||||||
that was imported (e.g. ``pkg.mod``), while :func:`__import__` returns the
|
that was imported (e.g. ``pkg.mod``), while :func:`__import__` returns the
|
||||||
top-level package or module (e.g. ``pkg``).
|
top-level package or module (e.g. ``pkg``).
|
||||||
|
|
||||||
|
.. function:: find_loader(name, path=None)
|
||||||
|
|
||||||
|
Find the loader for a module, optionally within the specified *path*. If the
|
||||||
|
module is in :attr:`sys.modules`, then ``sys.modules[name].__loader__`` is
|
||||||
|
returned (unless the loader would be ``None``, in which case
|
||||||
|
:exc:`ValueError` is raised). Otherwise a search using :attr:`sys.meta_path`
|
||||||
|
is done. ``None`` is returned if no loader is found.
|
||||||
|
|
||||||
|
A dotted name does not have its parent's implicitly imported. If that is
|
||||||
|
desired (although not nessarily required to find the loader, it will most
|
||||||
|
likely be needed if the loader actually is used to load the module), then
|
||||||
|
you will have to import the packages containing the module prior to calling
|
||||||
|
this function.
|
||||||
|
|
||||||
.. function:: invalidate_caches()
|
.. function:: invalidate_caches()
|
||||||
|
|
||||||
Invalidate the internal caches of the finders stored at
|
Invalidate the internal caches of the finders stored at
|
||||||
|
|
|
@ -29,6 +29,30 @@ def invalidate_caches():
|
||||||
finder.invalidate_caches()
|
finder.invalidate_caches()
|
||||||
|
|
||||||
|
|
||||||
|
def find_loader(name, path=None):
|
||||||
|
"""Find the loader for the specified module.
|
||||||
|
|
||||||
|
First, sys.modules is checked to see if the module was already imported. If
|
||||||
|
so, then sys.modules[name].__loader__ is returned. If that happens to be
|
||||||
|
set to None, then ValueError is raised. If the module is not in
|
||||||
|
sys.modules, then sys.meta_path is searched for a suitable loader with the
|
||||||
|
value of 'path' given to the finders. None is returned if no loader could
|
||||||
|
be found.
|
||||||
|
|
||||||
|
Dotted names do not have their parent packages implicitly imported.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
loader = sys.modules[name].__loader__
|
||||||
|
if loader is None:
|
||||||
|
raise ValueError('{}.__loader__ is None'.format(name))
|
||||||
|
else:
|
||||||
|
return loader
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
return _bootstrap._find_module(name, path)
|
||||||
|
|
||||||
|
|
||||||
def import_module(name, package=None):
|
def import_module(name, package=None):
|
||||||
"""Import a module.
|
"""Import a module.
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,54 @@ class ImportModuleTests(unittest.TestCase):
|
||||||
self.assertEqual(b_load_count, 1)
|
self.assertEqual(b_load_count, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class FindLoaderTests(unittest.TestCase):
|
||||||
|
|
||||||
|
class FakeMetaFinder:
|
||||||
|
@staticmethod
|
||||||
|
def find_module(name, path=None): return name, path
|
||||||
|
|
||||||
|
def test_sys_modules(self):
|
||||||
|
# If a module with __loader__ is in sys.modules, then return it.
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = imp.new_module(name)
|
||||||
|
loader = 'a loader!'
|
||||||
|
module.__loader__ = loader
|
||||||
|
sys.modules[name] = module
|
||||||
|
found = importlib.find_loader(name)
|
||||||
|
self.assertEqual(loader, found)
|
||||||
|
|
||||||
|
def test_sys_modules_loader_is_None(self):
|
||||||
|
# If sys.modules[name].__loader__ is None, raise ValueError.
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
module = imp.new_module(name)
|
||||||
|
module.__loader__ = None
|
||||||
|
sys.modules[name] = module
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
importlib.find_loader(name)
|
||||||
|
|
||||||
|
def test_success(self):
|
||||||
|
# Return the loader found on sys.meta_path.
|
||||||
|
name = 'some_mod'
|
||||||
|
with util.uncache(name):
|
||||||
|
with util.import_state(meta_path=[self.FakeMetaFinder]):
|
||||||
|
self.assertEqual((name, None), importlib.find_loader(name))
|
||||||
|
|
||||||
|
def test_success_path(self):
|
||||||
|
# Searching on a path should work.
|
||||||
|
name = 'some_mod'
|
||||||
|
path = 'path to some place'
|
||||||
|
with util.uncache(name):
|
||||||
|
with util.import_state(meta_path=[self.FakeMetaFinder]):
|
||||||
|
self.assertEqual((name, path),
|
||||||
|
importlib.find_loader(name, path))
|
||||||
|
|
||||||
|
def test_nothing(self):
|
||||||
|
# None is returned upon failure to find a loader.
|
||||||
|
self.assertIsNone(importlib.find_loader('nevergoingtofindthismodule'))
|
||||||
|
|
||||||
|
|
||||||
class InvalidateCacheTests(unittest.TestCase):
|
class InvalidateCacheTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_method_called(self):
|
def test_method_called(self):
|
||||||
|
@ -114,7 +162,7 @@ class InvalidateCacheTests(unittest.TestCase):
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
from test.support import run_unittest
|
from test.support import run_unittest
|
||||||
run_unittest(ImportModuleTests)
|
run_unittest(ImportModuleTests, FindLoaderTests, InvalidateCacheTests)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -39,8 +39,10 @@ Instances of this class have the following instance variables:
|
||||||
lineno -- the line in the file on which the class statement occurred
|
lineno -- the line in the file on which the class statement occurred
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import io
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import imp
|
import importlib
|
||||||
import tokenize
|
import tokenize
|
||||||
from token import NAME, DEDENT, OP
|
from token import NAME, DEDENT, OP
|
||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
|
@ -133,19 +135,24 @@ def _readmodule(module, path, inpackage=None):
|
||||||
# Search the path for the module
|
# Search the path for the module
|
||||||
f = None
|
f = None
|
||||||
if inpackage is not None:
|
if inpackage is not None:
|
||||||
f, fname, (_s, _m, ty) = imp.find_module(module, path)
|
search_path = path
|
||||||
else:
|
else:
|
||||||
f, fname, (_s, _m, ty) = imp.find_module(module, path + sys.path)
|
search_path = path + sys.path
|
||||||
if ty == imp.PKG_DIRECTORY:
|
loader = importlib.find_loader(fullmodule, search_path)
|
||||||
dict['__path__'] = [fname]
|
fname = loader.get_filename(fullmodule)
|
||||||
path = [fname] + path
|
|
||||||
f, fname, (_s, _m, ty) = imp.find_module('__init__', [fname])
|
|
||||||
_modules[fullmodule] = dict
|
_modules[fullmodule] = dict
|
||||||
if ty != imp.PY_SOURCE:
|
if loader.is_package(fullmodule):
|
||||||
|
dict['__path__'] = [os.path.dirname(fname)]
|
||||||
|
try:
|
||||||
|
source = loader.get_source(fullmodule)
|
||||||
|
if source is None:
|
||||||
|
return dict
|
||||||
|
except (AttributeError, ImportError):
|
||||||
# not Python source, can't do anything with this module
|
# not Python source, can't do anything with this module
|
||||||
f.close()
|
|
||||||
return dict
|
return dict
|
||||||
|
|
||||||
|
f = io.StringIO(source)
|
||||||
|
|
||||||
stack = [] # stack of (class, indent) pairs
|
stack = [] # stack of (class, indent) pairs
|
||||||
|
|
||||||
g = tokenize.generate_tokens(f.readline)
|
g = tokenize.generate_tokens(f.readline)
|
||||||
|
|
|
@ -23,6 +23,8 @@ Core and Builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- Issue #13959: Introduce importlib.find_loader().
|
||||||
|
|
||||||
- Issue #14082: shutil.copy2() now copies extended attributes, if possible.
|
- Issue #14082: shutil.copy2() now copies extended attributes, if possible.
|
||||||
Patch by Hynek Schlawack.
|
Patch by Hynek Schlawack.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue