1.get_importer(path_item)
get_importer方法主要返回一个importer对象,当系统缓存sys.path_importer_cache中根据path可以取到时,直接返回,如果取不到则创建一个ImpWrapper的类对象作为importer,并将此值返回,返回对象的类型是pkgutil模块中ImpImporter类的一个实例。ImpWrappe类和ImpImporter类的实现方式是一样的,功能也一致。
def get_importer(path_item):
"""Retrieve a PEP 302 "importer" for the given path item
If there is no importer, this returns a wrapper around the builtin import
machinery. The returned importer is only cached if it was created by a
path hook.
"""
try:
importer = sys.path_importer_cache[path_item]
except KeyError:
for hook in sys.path_hooks:
try:
importer = hook(path_item)
except ImportError:
pass
else:
break
else:
importer = None
sys.path_importer_cache.setdefault(path_item,importer)
if importer is None:
try:
importer = ImpWrapper(path_item)
except ImportError:
pass
return importer
2._find_adapter(registry, ob)
参数registry为pkg_resource模块的全局变量_distribution_finders,其值是一个字典{<class pkgutil.ImpImporter at 0x7f14cd474c80>: <function find_on_path at 0x7f14cd4817d0>, <type 'object'>: <function find_nothing at 0x7f14cd486230>, <type 'zipimport.zipimporter'>: <function find_in_zip at 0x7f14cd4862a8>},键是对应的对象类型,值为对应的函数对象;ob为ImpImporter类的实例对象。
其作用是根据ob的属性,从_distribution_finders变量中返回对应的函数对象,判断方法:使用getattr(ob, '__class__', type(ob))方法得到ob实例的类名称,接着使用_get_mro方法,判断ob实例对象的类是否为元类type的实例,如果不是,则为该类添加超类object,并返回该类的object属性,返回值为元组,例如(<class pkgutil.ImpImporter at 0x7f667ab6bc18>, <type 'object'>)。
def _get_mro(cls):
"""Get an mro for a type or classic class"""
if not isinstance(cls,type):
class cls(cls,object): pass
return cls.__mro__[1:]
return cls.__mro__
def _find_adapter(registry, ob):
"""Return an adapter factory for `ob` from `registry`"""
for t in _get_mro(getattr(ob, '__class__', type(ob))):
if t in registry:
return registry[t]
3._get_mro(cls)
根据cls的类型,为cls类增加经典类object或None。
def _get_mro(cls):
"""Get an mro for a type or classic class"""
if not isinstance(cls,type):
class cls(cls,object): pass
return cls.__mro__[1:]
return cls.__mro__
4.find_on_path(importer, path_item, only=False)
find_on_path根据当前path_item路径过滤出path_item路径的一级路径下,包名以.egg-info或.dist-info结尾的包,并调用Distribution.from_location( path_item,entry,metadata,precedence=DEVELOP_DIST )来返回该包的名称和版本号。
def find_on_path(importer, path_item, only=False):
"""Yield distributions accessible on a sys.path directory"""
path_item = _normalize_cached(path_item)
if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
if path_item.lower().endswith('.egg'):
# unpacked egg
yield Distribution.from_filename(
path_item, metadata=PathMetadata(
path_item, os.path.join(path_item,'EGG-INFO')
)
)
else:
# scan for .egg and .egg-info in directory
for entry in os.listdir(path_item):
lower = entry.lower()
if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
fullpath = os.path.join(path_item, entry)
if os.path.isdir(fullpath):
# egg-info directory, allow getting metadata
metadata = PathMetadata(path_item, fullpath)
else:
metadata = FileMetadata(fullpath)
yield Distribution.from_location(
path_item,entry,metadata,precedence=DEVELOP_DIST
)
elif not only and lower.endswith('.egg'):
for dist in find_distributions(os.path.join(path_item, entry)):
yield dist
elif not only and lower.endswith('.egg-link'):
entry_file = open(os.path.join(path_item, entry))
try:
entry_lines = entry_file.readlines()
finally:
entry_file.close()
for line in entry_lines:
if not line.strip(): continue
for item in find_distributions(os.path.join(path_item,line.rstrip())):
yield item
break
5.find_distributions(path_item, only=False)
调用get_importer(path_item)方法返回一个ImpImporter类的一个实例对象,调用_find_adapter(_distribution_finders, importer)方法,根据importer的类型在_distribution_finders中选择正确的函数。
def find_distributions(path_item, only=False):
"""Yield distributions accessible via `path_item`"""
importer = get_importer(path_item)
finder = _find_adapter(_distribution_finders, importer)
return finder(importer, path_item, only)