pkgutil模块用于导入。
loader加载器
下面以一个服务函数get_loader()来解析查找模块加载器过程
def get_loader(module_or_name):
"""Get a PEP 302 "loader" object for module_or_name
If the module or package is accessible via the normal import
mechanism, a wrapper around the relevant part of that machinery
is returned. Returns None if the module cannot be found or imported.
If the named module is not already imported, its containing package
(if any) is imported, in order to establish the package __path__.
This function uses iter_importers(), and is thus subject to the same
limitations regarding platform-specific special import locations such
as the Windows registry.
"""
if module_or_name in sys.modules:
module_or_name = sys.modules[module_or_name]
if isinstance(module_or_name, ModuleType):
module = module_or_name
loader = getattr(module, '__loader__', None)
if loader is not None:
return loader
fullname = module.__name__
else:
fullname = module_or_name
return find_loader(fullname)
首先,检查模块名是否存在于sys.modules列表中,则获取该模块类型。
其次,检查入参模块类型是否属于模块类型,如果是,并且该模块有__loader__属性,则直接返回该属性的加载器
最后,调用find_loader()查找加载器
def find_loader(fullname):
"""Find a PEP 302 "loader" object for fullname
If fullname contains dots, path must be the containing package's __path__.
Returns None if the module cannot be found or imported. This function uses
iter_importers(), and is thus subject to the same limitations regarding
platform-specific special import locations such as the Windows registry.
"""
for importer in iter_importers(fullname):
loader = importer.find_module(fullname)
if loader is not None:
return loader
return None
def iter_importers(fullname=""):
if fullname.startswith('.'):
raise ImportError("Relative module names not supported")
if '.' in fullname:
# Get the containing package's __path__
pkg = '.'.join(fullname.split('.')[:-1])
if pkg not in sys.modules:
__import__(pkg)
path = getattr(sys.modules[pkg], '__path__', None) or []
else:
for importer in sys.meta_path:
yield importer
path = sys.path
for item in path:
yield get_importer(item)
if '.' not in fullname:
yield ImpImporter()
fullname分为2种情况:
- 带.分割的路径。先获取目录路径,如果该路径没有加载过,则先加载,然后从sys.modules种获取该路径的模块,如果该模块存在__path__,则获取__path__作为后面查找加载器的路径查找所有加载器
- 不带.分割的路径。如果sys.meta_path中存在加载器,则返回加载器,返回里面所有加载器后,再以sys.path中所有路径查找所有加载器
def get_importer(path_item):
"""Retrieve a PEP 302 importer for the given path item
The returned importer is cached in sys.path_importer_cache
if it was newly created by a path hook.
If there is no importer, a wrapper around the basic import
machinery is returned. This wrapper is never inserted into
the importer cache (None is inserted instead).
The cache (or part of it) can be cleared manually if a
rescan of sys.path_hooks is necessary.
"""
try:
importer = sys.path_importer_cache[path_item]
except KeyError:
for path_hook in sys.path_hooks:
try:
importer = path_hook(path_item)
break
except ImportError:
pass
else:
importer = None
sys.path_importer_cache.setdefault(path_item, importer)
if importer is None:
try:
importer = ImpImporter(path_item)
except ImportError:
importer = None
return importer
查找sys.path_importer_cache(sys.path_importer_cache类型是字典)。找不到就会抛异常,然后遍历sys.path_hooks中的对象并调用之,找到一个importer则不再遍历
如果前面找到的importer为空,则返回一个默认ImpImporter