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
|
||||
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()
|
||||
|
||||
Invalidate the internal caches of the finders stored at
|
||||
|
|
|
@ -29,6 +29,30 @@ def 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):
|
||||
"""Import a module.
|
||||
|
||||
|
|
|
@ -85,6 +85,54 @@ class ImportModuleTests(unittest.TestCase):
|
|||
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):
|
||||
|
||||
def test_method_called(self):
|
||||
|
@ -114,7 +162,7 @@ class InvalidateCacheTests(unittest.TestCase):
|
|||
|
||||
def test_main():
|
||||
from test.support import run_unittest
|
||||
run_unittest(ImportModuleTests)
|
||||
run_unittest(ImportModuleTests, FindLoaderTests, InvalidateCacheTests)
|
||||
|
||||
|
||||
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
|
||||
"""
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import imp
|
||||
import importlib
|
||||
import tokenize
|
||||
from token import NAME, DEDENT, OP
|
||||
from operator import itemgetter
|
||||
|
@ -133,19 +135,24 @@ def _readmodule(module, path, inpackage=None):
|
|||
# Search the path for the module
|
||||
f = None
|
||||
if inpackage is not None:
|
||||
f, fname, (_s, _m, ty) = imp.find_module(module, path)
|
||||
search_path = path
|
||||
else:
|
||||
f, fname, (_s, _m, ty) = imp.find_module(module, path + sys.path)
|
||||
if ty == imp.PKG_DIRECTORY:
|
||||
dict['__path__'] = [fname]
|
||||
path = [fname] + path
|
||||
f, fname, (_s, _m, ty) = imp.find_module('__init__', [fname])
|
||||
search_path = path + sys.path
|
||||
loader = importlib.find_loader(fullmodule, search_path)
|
||||
fname = loader.get_filename(fullmodule)
|
||||
_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
|
||||
f.close()
|
||||
return dict
|
||||
|
||||
f = io.StringIO(source)
|
||||
|
||||
stack = [] # stack of (class, indent) pairs
|
||||
|
||||
g = tokenize.generate_tokens(f.readline)
|
||||
|
|
Loading…
Reference in New Issue