def find_spec(cls, fullname, path=None, target=None):
"""find the module on sys.path or 'path' based on sys.path_hooks and
sys.path_importer_cache."""
if path is None:
path = sys.path
spec = cls._get_spec(fullname, path, target)
if spec is None:
return None
elif spec.loader is None:
namespace_path = spec.submodule_search_locations
if namespace_path:
# We found at least one namespace path. Return a
# spec which can create the namespace package.
spec.origin = 'namespace'
spec.submodule_search_locations = _NamespacePath(fullname, namespace_path, cls._get_spec)
return spec
else:
return None
else:
return spec
# to match importlib API
python类path_hooks()的实例源码
def activate():
"""Install the path-based import components."""
# We should plug filefinder first to avoid plugging ROSDirectoryFinder, when it is not a ROS thing...
filefinder2.activate()
if ros_path_hook not in sys.path_hooks:
# We need to be before FileFinder to be able to find our '.msg' and '.srv' files without making a namespace package
# Note this must be early in the path_hook list, since we change the logic
# and a namespace package becomes a ros importable package.
sys.path_hooks.insert(filefinder2.get_filefinder_index_in_path_hooks(), ros_path_hook)
if ROSPathFinder not in sys.meta_path:
# adding metahook, before the usual pathfinder, to avoid interferences with python namespace mechanism...
sys.meta_path.insert(filefinder2.get_pathfinder_index_in_meta_hooks(), ROSPathFinder)
# Resetting sys.path_importer_cache
# to support the case where we have an implicit (msg/srv) package inside an already loaded package,
# since we need to replace the default importer.
sys.path_importer_cache.clear()
def _check_importer_for_path(name, path_item):
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:
return imp.find_module(name, [path_item])
except ImportError:
return None
return importer.find_module(name)
def testImpWrapper(self):
i = ImpWrapper()
sys.meta_path.append(i)
sys.path_hooks.insert(0, ImpWrapper)
mnames = (
"colorsys", "urllib.parse", "distutils.core", "sys",
)
for mname in mnames:
parent = mname.split(".")[0]
for n in list(sys.modules):
if n.startswith(parent):
del sys.modules[n]
for mname in mnames:
m = __import__(mname, globals(), locals(), ["__dummy__"])
# to make sure we actually handled the import
self.assertTrue(hasattr(m, "__loader__"))
def test_None_on_sys_path(self):
# Putting None in sys.path[0] caused an import regression from Python
# 3.2: http://bugs.python.org/issue16514
new_path = sys.path[:]
new_path.insert(0, None)
new_path_importer_cache = sys.path_importer_cache.copy()
new_path_importer_cache.pop(None, None)
new_path_hooks = [zipimport.zipimporter,
_bootstrap.FileFinder.path_hook(
*_bootstrap._get_supported_file_loaders())]
missing = object()
email = sys.modules.pop('email', missing)
try:
with util.import_state(meta_path=sys.meta_path[:],
path=new_path,
path_importer_cache=new_path_importer_cache,
path_hooks=new_path_hooks):
module = import_module('email')
self.assertIsInstance(module, ModuleType)
finally:
if email is not missing:
sys.modules['email'] = email
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.
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)
sys.path_importer_cache.setdefault(path_item, importer)
break
except ImportError:
pass
else:
importer = None
return importer
def test_finder_with_failing_find_module(self):
# PathEntryFinder with find_module() defined should work.
# Issue #20763.
class Finder:
path_location = 'test_finder_with_find_module'
def __init__(self, path):
if path != self.path_location:
raise ImportError
@staticmethod
def find_module(fullname):
return None
with util.import_state(path=[Finder.path_location]+sys.path[:],
path_hooks=[Finder]):
self.machinery.PathFinder.find_spec('importlib')
def test_load_with_path_hook(self):
class DummyPathHook(object):
def __init__(self, path):
if path != 'dummy/path':
raise ImportError
def find_module(self, unused_fullname):
return self
def load_module(self, fullname):
return imp.new_module('fake name: %s' % fullname)
self.test_policies['distutils.util'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
sys.path_hooks = [DummyPathHook]
util = self.hook.load_module('distutils.util')
self.assertEqual('fake name: distutils.util', util.__name__)
def test_load_with_path_hook_cant_find(self):
class DummyPathHook(object):
def __init__(self, path):
if path != 'dummy/path':
raise ImportError
def find_module(self, unused_fullname):
return None
def load_module(self, fullname):
raise ImportError
self.test_policies['distutils.util'] = sandbox.ModuleOverridePolicy(
None, [], {}, default_pass_through=True)
sys.path_hooks = [DummyPathHook]
util = self.hook.load_module('distutils.util')
self.assertEqual('distutils.util', util.__name__)
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.
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)
sys.path_importer_cache.setdefault(path_item, importer)
break
except ImportError:
pass
else:
importer = None
return importer
def test_finder_with_failing_find_module(self):
# PathEntryFinder with find_module() defined should work.
# Issue #20763.
class Finder:
path_location = 'test_finder_with_find_module'
def __init__(self, path):
if path != self.path_location:
raise ImportError
@staticmethod
def find_module(fullname):
return None
with util.import_state(path=[Finder.path_location]+sys.path[:],
path_hooks=[Finder]):
self.machinery.PathFinder.find_spec('importlib')
def _check_importer_for_path(name, path_item):
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:
return imp.find_module(name, [path_item])
except ImportError:
return None
return importer.find_module(name)
def _get_importer(path_name):
"""Python version of PyImport_GetImporter C API function"""
cache = sys.path_importer_cache
try:
importer = cache[path_name]
except KeyError:
# Not yet cached. Flag as using the
# standard machinery until we finish
# checking the hooks
cache[path_name] = None
for hook in sys.path_hooks:
try:
importer = hook(path_name)
break
except ImportError:
pass
else:
# The following check looks a bit odd. The trick is that
# NullImporter throws ImportError if the supplied path is a
# *valid* directory entry (and hence able to be handled
# by the standard import machinery)
try:
importer = imp.NullImporter(path_name)
except ImportError:
return None
cache[path_name] = importer
return importer
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
def _get_importer(path_name):
"""Python version of PyImport_GetImporter C API function"""
cache = sys.path_importer_cache
try:
importer = cache[path_name]
except KeyError:
# Not yet cached. Flag as using the
# standard machinery until we finish
# checking the hooks
cache[path_name] = None
for hook in sys.path_hooks:
try:
importer = hook(path_name)
break
except ImportError:
pass
else:
# The following check looks a bit odd. The trick is that
# NullImporter raises ImportError if the supplied path is a
# *valid* directory entry (and hence able to be handled
# by the standard import machinery)
try:
importer = imp.NullImporter(path_name)
except ImportError:
return None
cache[path_name] = importer
return importer
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
def _path_hooks(cls, path): # from importlib.PathFinder
"""Search sys.path_hooks for a finder for 'path'."""
if sys.path_hooks is not None and not sys.path_hooks:
warnings.warn('sys.path_hooks is empty', ImportWarning)
for hook in sys.path_hooks:
try:
return hook(path)
except ImportError:
continue
else:
return None
def find_module(cls, fullname, path=None):
"""find the module on sys.path or 'path' based on sys.path_hooks and
sys.path_importer_cache.
This method is for python2 only
"""
spec = cls.find_spec(fullname, path)
if spec is None:
return None
elif spec.loader is None and spec.submodule_search_locations:
# Here we need to create a namespace loader to handle namespaces since python2 doesn't...
return NamespaceLoader2(spec.name, spec.submodule_search_locations)
else:
return spec.loader
def get_filefinder_index_in_path_hooks():
return sys.path_hooks.index(path_hook)
# Making the activation explicit for now
def activate():
"""Install the path-based import components."""
if path_hook not in sys.path_hooks:
sys.path_hooks.append(path_hook)
# Resetting sys.path_importer_cache values,
# to support the case where we have an implicit package inside an already loaded package,
# since we need to replace the default importer.
sys.path_importer_cache.clear()
if PathFinder2 not in sys.meta_path:
# Setting up the meta_path to change package finding logic
sys.meta_path.append(PathFinder2)
def get_filefinder_index_in_path_hooks():
# we look for the hook we detected there at import time (should usually be Filefinder
# if there isnt too many import mess...
return sys.path_hooks.index(ffhook)
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
def _get_importer(path_name):
"""Python version of PyImport_GetImporter C API function"""
cache = sys.path_importer_cache
try:
importer = cache[path_name]
except KeyError:
# Not yet cached. Flag as using the
# standard machinery until we finish
# checking the hooks
cache[path_name] = None
for hook in sys.path_hooks:
try:
importer = hook(path_name)
break
except ImportError:
pass
else:
# The following check looks a bit odd. The trick is that
# NullImporter raises ImportError if the supplied path is a
# *valid* directory entry (and hence able to be handled
# by the standard import machinery)
try:
importer = imp.NullImporter(path_name)
except ImportError:
return None
cache[path_name] = importer
return importer
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
def _get_importer(path_name):
"""Python version of PyImport_GetImporter C API function"""
cache = sys.path_importer_cache
try:
importer = cache[path_name]
except KeyError:
# Not yet cached. Flag as using the
# standard machinery until we finish
# checking the hooks
cache[path_name] = None
for hook in sys.path_hooks:
try:
importer = hook(path_name)
break
except ImportError:
pass
else:
# The following check looks a bit odd. The trick is that
# NullImporter raises ImportError if the supplied path is a
# *valid* directory entry (and hence able to be handled
# by the standard import machinery)
try:
importer = imp.NullImporter(path_name)
except ImportError:
return None
cache[path_name] = importer
return importer
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
def _install():
"""Install the path-based import components."""
supported_loaders = get_supported_ros_loaders()
sys.path_hooks.extend([ROSDirectoryFinder.path_hook(*supported_loaders)])
# TODO : sys.meta_path.append(DistroFinder)
# Useless ?
# _ros_finder_instance_obsolete_python = ROSImportFinder
def activate(rosdistro_path=None, *workspaces):
global ros_distro_finder
if rosdistro_path is None: # autodetect most recent installed distro
if os.path.exists('/opt/ros/lunar'):
rosdistro_path = '/opt/ros/lunar'
elif os.path.exists('/opt/ros/kinetic'):
rosdistro_path = '/opt/ros/kinetic'
elif os.path.exists('/opt/ros/jade'):
rosdistro_path = '/opt/ros/jade'
elif os.path.exists('/opt/ros/indigo'):
rosdistro_path = '/opt/ros/indigo'
else:
raise ImportError(
"No ROS distro detected on this system. Please specify the path when calling ROSImportMetaFinder()")
ros_distro_finder = ROSPathFinder(rosdistro_path, *workspaces)
sys.meta_path.append(ros_distro_finder)
#if sys.version_info >= (2, 7, 12): # TODO : which exact version matters ?
# We need to be before FileFinder to be able to find our (non .py[c]) files
# inside, maybe already imported, python packages...
sys.path_hooks.insert(1, ROSDirectoryFinder)
# else: # older (trusty) version
# sys.path_hooks.append(_ros_finder_instance_obsolete_python)
for hook in sys.path_hooks:
print('Path hook: {}'.format(hook))
# TODO : mix that with ROS PYTHONPATH shenanigans... to enable the finder only for 'ROS aware' paths
if paths:
sys.path.append(*paths)
def deactivate(*paths):
""" CAREFUL : even if we remove our path_hooks, the created finder are still cached in sys.path_importer_cache."""
#if sys.version_info >= (2, 7, 12): # TODO : which exact version matters ?
sys.path_hooks.remove(ROSImportFinder)
# else: # older (trusty) version
# sys.path_hooks.remove(_ros_finder_instance_obsolete_python)
if paths:
sys.path.remove(*paths)
sys.meta_path.remove(ros_distro_finder)
def deactivate(*paths):
""" CAREFUL : even if we remove our path_hooks, the created finder are still cached in sys.path_importer_cache."""
#if sys.version_info >= (2, 7, 12): # TODO : which exact version matters ?
sys.path_hooks.remove(ROSImportFinder)
# else: # older (trusty) version
# sys.path_hooks.remove(_ros_finder_instance_obsolete_python)
if paths:
sys.path.remove(*paths)
sys.meta_path.remove(ros_distro_finder)