Load Python C extensions of external packages correctly (#391)

This commit is contained in:
Gautham 2022-05-09 10:19:50 +05:30 committed by GitHub
parent b30f5c0c4f
commit 363d2ec436
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 82 additions and 196 deletions

View file

@ -32,10 +32,6 @@ def _wrap(new, old):
new.__dict__.update(old.__dict__) new.__dict__.update(old.__dict__)
def _new_module(name):
return type(sys)(name)
# Module-level locking ######################################################## # Module-level locking ########################################################
# A dict mapping module names to weakrefs of _ModuleLock instances # A dict mapping module names to weakrefs of _ModuleLock instances
@ -230,7 +226,7 @@ def _verbose_message(message, *args, verbosity=1):
def _requires_builtin(fxn): def _requires_builtin(fxn):
"""Decorator to verify the named module is built-in.""" """Decorator to verify the named module is built-in."""
def _requires_builtin_wrapper(self, fullname): def _requires_builtin_wrapper(self, fullname):
if fullname not in sys.builtin_module_names: if fullname not in BUILTIN_MODULE_NAMES:
raise ImportError('{!r} is not a built-in module'.format(fullname), raise ImportError('{!r} is not a built-in module'.format(fullname),
name=fullname) name=fullname)
return fxn(self, fullname) return fxn(self, fullname)
@ -277,29 +273,21 @@ def _module_repr(module):
return loader.module_repr(module) return loader.module_repr(module)
except Exception: except Exception:
pass pass
try: spec = getattr(module, "__spec__", None)
spec = module.__spec__ if spec is not None:
except AttributeError: return _module_repr_from_spec(spec)
pass
else:
if spec is not None:
return _module_repr_from_spec(spec)
# We could use module.__class__.__name__ instead of 'module' in the # We could use module.__class__.__name__ instead of 'module' in the
# various repr permutations. # various repr permutations.
try: name = getattr(module,"__name__", '?')
name = module.__name__ filename = getattr(module, "__file__", None)
except AttributeError: if filename is not None:
name = '?' return '<module {!r} from {!r}>'.format(name, filename)
try: else:
filename = module.__file__
except AttributeError:
if loader is None: if loader is None:
return '<module {!r}>'.format(name) return '<module {!r}>'.format(name)
else: else:
return '<module {!r} ({!r})>'.format(name, loader) return '<module {!r} ({!r})>'.format(name, loader)
else:
return '<module {!r} from {!r}>'.format(name, filename)
class _installed_safely: class _installed_safely:
@ -458,106 +446,51 @@ def spec_from_loader(name, loader, *, origin=None, is_package=None):
def _spec_from_module(module, loader=None, origin=None): def _spec_from_module(module, loader=None, origin=None):
# This function is meant for use in _setup(). # This function is meant for use in _setup().
try:
spec = module.__spec__
except AttributeError:
pass
else:
if spec is not None:
return spec
name = module.__name__ name = module.__name__
if loader is None: loader = loader or getattr(module, "__loader__", None)
try: location = getattr(module, "__file__", None)
loader = module.__loader__ origin = origin or location or getattr(loader, "_ORIGIN", None)
except AttributeError: cached = getattr(module, "__cached__", None)
# loader will stay None. submodule_search_locations = getattr(module, "__path__", None)
pass if submodule_search_locations is not None:
try: submodule_search_locations = list(submodule_search_locations)
location = module.__file__
except AttributeError:
location = None
if origin is None:
if location is None:
try:
origin = loader._ORIGIN
except AttributeError:
origin = None
else:
origin = location
try:
cached = module.__cached__
except AttributeError:
cached = None
try:
submodule_search_locations = list(module.__path__)
except AttributeError:
submodule_search_locations = None
spec = ModuleSpec(name, loader, origin=origin) spec = ModuleSpec(name, loader, origin=origin)
spec._set_fileattr = False if location is None else True spec._set_fileattr = location is not None
spec.cached = cached spec.cached = cached
spec.submodule_search_locations = submodule_search_locations spec.submodule_search_locations = submodule_search_locations
return spec return spec
def _init_module_attrs(spec, module, *, override=False): def _init_module_attrs(spec, module, *, override=False):
# The passed-in module may be not support attribute assignment, if override:
# in which case we simply don't set the attributes. module.__name__ = spec.name
# __name__ module.__loader__ = spec.loader
if (override or getattr(module, '__name__', None) is None): module.__package__ = spec.parent
try: module.__path__ = None or spec.submodule_search_locations
module.__name__ = spec.name if spec.has_location:
except AttributeError: module.__file__ = None or spec.origin
pass module.__cached__ = None or spec.cached
# __loader__ else:
if override or getattr(module, '__loader__', None) is None: module.__name__ = getattr(module, "__name__", None) or spec.name
loader = spec.loader module.__loader__ = getattr(module, "__loader__", None) or spec.loader
if loader is None: module.__package__ = getattr(module, "__package__", None) or spec.parent
# A backward compatibility hack. module.__path__ = getattr(module, "__path__", None) or spec.submodule_search_locations
if spec.submodule_search_locations is not None: if spec.has_location:
if _bootstrap_external is None: module.__file__ = getattr(module, "__file__", None) or spec.origin
raise NotImplementedError module.__cached__ = getattr(module, "__cached__", None) or spec.cached
_NamespaceLoader = _bootstrap_external._NamespaceLoader
loader = _NamespaceLoader.__new__(_NamespaceLoader) module.__spec__ = getattr(module, "__spec__", None) or spec
loader._path = spec.submodule_search_locations if module.__loader__ is None:
try: # A backward compatibility hack.
module.__loader__ = loader
except AttributeError:
pass
# __package__
if override or getattr(module, '__package__', None) is None:
try:
module.__package__ = spec.parent
except AttributeError:
pass
# __spec__
try:
module.__spec__ = spec
except AttributeError:
pass
# __path__
if override or getattr(module, '__path__', None) is None:
if spec.submodule_search_locations is not None: if spec.submodule_search_locations is not None:
try: if _bootstrap_external is None:
module.__path__ = spec.submodule_search_locations raise NotImplementedError
except AttributeError: _NamespaceLoader = _bootstrap_external._NamespaceLoader
pass module.__loader__ = _NamespaceLoader.__new__(_NamespaceLoader)
# __file__/__cached__ module.__loader__._path = spec.submodule_search_locations
if spec.has_location:
if override or getattr(module, '__file__', None) is None:
try:
module.__file__ = spec.origin
except AttributeError:
pass
if override or getattr(module, '__cached__', None) is None:
if spec.cached is not None:
try:
module.__cached__ = spec.cached
except AttributeError:
pass
return module return module
@ -573,7 +506,7 @@ def module_from_spec(spec):
raise ImportError('loaders that define exec_module() ' raise ImportError('loaders that define exec_module() '
'must also define create_module()') 'must also define create_module()')
if module is None: if module is None:
module = _new_module(spec.name) module = type(sys)(spec.name)
_init_module_attrs(spec, module) _init_module_attrs(spec, module)
return module return module
@ -708,7 +641,7 @@ class BuiltinImporter:
def find_spec(cls, fullname, path=None, target=None): def find_spec(cls, fullname, path=None, target=None):
if path is not None: if path is not None:
return None return None
if _imp.is_builtin(fullname): if fullname in BUILTIN_MODULE_NAMES:
return spec_from_loader(fullname, cls, origin='built-in') return spec_from_loader(fullname, cls, origin='built-in')
else: else:
return None return None
@ -728,7 +661,7 @@ class BuiltinImporter:
@classmethod @classmethod
def create_module(self, spec): def create_module(self, spec):
"""Create a built-in module""" """Create a built-in module"""
if spec.name not in sys.builtin_module_names: if spec.name not in BUILTIN_MODULE_NAMES:
raise ImportError('{!r} is not a built-in module'.format(spec.name), raise ImportError('{!r} is not a built-in module'.format(spec.name),
name=spec.name) name=spec.name)
return _call_with_frames_removed(_imp.create_builtin, spec) return _call_with_frames_removed(_imp.create_builtin, spec)
@ -949,6 +882,19 @@ def _find_and_load_unlocked(name, import_):
msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent) msg = (_ERR_MSG + '; {!r} is not a package').format(name, parent)
raise ModuleNotFoundError(msg, name=name) from None raise ModuleNotFoundError(msg, name=name) from None
spec = _find_spec(name, path) spec = _find_spec(name, path)
if spec is None and name in BUILTIN_MODULE_NAMES:
# If this module is a C extension, the interpreter
# expects it to be a shared object located in path,
# and returns spec is None because it was not found.
#
# however, if it is a C extension, we can check if it
# is available using sys.builtin_module_names,
# because the APE is statically compiled.
#
# if the module is present as a builtin, we call
# BuiltinImporter with the full name (and no path)
# to create the module spec correctly.
spec = BuiltinImporter.find_spec(name)
if spec is None: if spec is None:
raise ModuleNotFoundError(_ERR_MSG.format(name), name=name) raise ModuleNotFoundError(_ERR_MSG.format(name), name=name)
else: else:
@ -1109,43 +1055,30 @@ def _setup(sys_module, _imp_module):
modules, those two modules must be explicitly passed in. modules, those two modules must be explicitly passed in.
""" """
global _imp, sys global _imp, sys, BUILTIN_MODULE_NAMES
_imp = _imp_module _imp = _imp_module
sys = sys_module sys = sys_module
BUILTIN_MODULE_NAMES = frozenset(sys.builtin_module_names)
# Set up the spec for existing builtin/frozen modules. # Set up the spec for existing builtin/frozen modules.
module_type = type(sys) module_type = type(sys)
for name, module in sys.modules.items(): for name, module in sys.modules.items():
if isinstance(module, module_type): if isinstance(module, module_type):
if name in sys.builtin_module_names: if name in BUILTIN_MODULE_NAMES:
loader = BuiltinImporter loader = BuiltinImporter
elif _imp.is_frozen(name): elif _imp.is_frozen(name):
loader = FrozenImporter loader = FrozenImporter
else: else:
continue continue
spec = _spec_from_module(module, loader) spec = getattr(module, "__spec__", None) or _spec_from_module(module, loader)
_init_module_attrs(spec, module) _init_module_attrs(spec, module)
# Directly load built-in modules needed during bootstrap. # Directly load built-in modules needed during bootstrap.
self_module = sys.modules[__name__] self_module = sys.modules[__name__]
for builtin_name in ('_warnings',): for builtin_name in ('_warnings', '_weakref'):
if builtin_name not in sys.modules: builtin_module = sys.modules.get(builtin_name, _builtin_from_name(builtin_name))
builtin_module = _builtin_from_name(builtin_name)
else:
builtin_module = sys.modules[builtin_name]
setattr(self_module, builtin_name, builtin_module) setattr(self_module, builtin_name, builtin_module)
setattr(self_module, '_thread', None)
# Directly load the _thread module (needed during bootstrap).
try:
thread_module = _builtin_from_name('_thread')
except ImportError:
# Python was built without threads
thread_module = None
setattr(self_module, '_thread', thread_module)
# Directly load the _weakref module (needed during bootstrap).
weakref_module = _builtin_from_name('_weakref')
setattr(self_module, '_weakref', weakref_module)
def _install(sys_module, _imp_module): def _install(sys_module, _imp_module):

View file

@ -98,8 +98,7 @@ def _path_isfile(path):
def _path_isdir(path): def _path_isdir(path):
"""Replacement for os.path.isdir.""" """Replacement for os.path.isdir."""
if not path: path = path or _os.getcwd()
path = _os.getcwd()
return _path_is_mode_type(path, 0o040000) return _path_is_mode_type(path, 0o040000)
@ -397,15 +396,11 @@ def _check_name(method):
raise ImportError('loader for %s cannot handle %s' % raise ImportError('loader for %s cannot handle %s' %
(self.name, name), name=name) (self.name, name), name=name)
return method(self, name, *args, **kwargs) return method(self, name, *args, **kwargs)
try: def _wrap(new, old):
_wrap = _bootstrap._wrap for replace in ['__module__', '__name__', '__qualname__', '__doc__']:
except NameError: if hasattr(old, replace):
# XXX yuck setattr(new, replace, getattr(old, replace))
def _wrap(new, old): new.__dict__.update(old.__dict__)
for replace in ['__module__', '__name__', '__qualname__', '__doc__']:
if hasattr(old, replace):
setattr(new, replace, getattr(old, replace))
new.__dict__.update(old.__dict__)
_wrap(_check_name_wrapper, method) _wrap(_check_name_wrapper, method)
return _check_name_wrapper return _check_name_wrapper
@ -1345,14 +1340,10 @@ def _fix_up_module(ns, name, pathname, cpathname=None):
loader = SourceFileLoader(name, pathname) loader = SourceFileLoader(name, pathname)
if not spec: if not spec:
spec = spec_from_file_location(name, pathname, loader=loader) spec = spec_from_file_location(name, pathname, loader=loader)
try: ns['__spec__'] = spec
ns['__spec__'] = spec ns['__loader__'] = loader
ns['__loader__'] = loader ns['__file__'] = pathname
ns['__file__'] = pathname ns['__cached__'] = cpathname
ns['__cached__'] = cpathname
except Exception:
# Not important enough to report.
pass
def _get_supported_file_loaders(): def _get_supported_file_loaders():
@ -1378,61 +1369,23 @@ def _setup(_bootstrap_module):
sys = _bootstrap.sys sys = _bootstrap.sys
_imp = _bootstrap._imp _imp = _bootstrap._imp
builtin_from_name = _bootstrap._builtin_from_name
# Directly load built-in modules needed during bootstrap. # Directly load built-in modules needed during bootstrap.
self_module = sys.modules[__name__] self_module = sys.modules[__name__]
for builtin_name in ('_io', '_warnings', 'builtins', 'marshal'): for builtin_name in ('_io', '_warnings', 'builtins', 'marshal', 'posix', '_weakref'):
if builtin_name not in sys.modules: setattr(self_module, builtin_name, sys.modules.get(builtin_name, builtin_from_name(builtin_name)))
builtin_module = _bootstrap._builtin_from_name(builtin_name)
else:
builtin_module = sys.modules[builtin_name]
setattr(self_module, builtin_name, builtin_module)
# Directly load the os module (needed during bootstrap). # Directly load the os module (needed during bootstrap).
os_details = ('posix', ['/']), ('nt', ['\\', '/']) os_details = ('posix', ['/']), ('nt', ['\\', '/'])
for builtin_os, path_separators in os_details: builtin_os, path_separators = os_details[0]
# Assumption made in _path_join() setattr(self_module, '_os', sys.modules.get(builtin_os, builtin_from_name(builtin_os)))
assert all(len(sep) == 1 for sep in path_separators) setattr(self_module, 'path_sep', path_separators[0])
path_sep = path_separators[0]
if builtin_os in sys.modules:
os_module = sys.modules[builtin_os]
break
else:
try:
os_module = _bootstrap._builtin_from_name(builtin_os)
break
except ImportError:
continue
else:
raise ImportError('importlib requires posix or nt')
setattr(self_module, '_os', os_module)
setattr(self_module, 'path_sep', path_sep)
setattr(self_module, 'path_separators', ''.join(path_separators)) setattr(self_module, 'path_separators', ''.join(path_separators))
setattr(self_module, '_thread', None)
# Directly load the _thread module (needed during bootstrap).
try:
thread_module = _bootstrap._builtin_from_name('_thread')
except ImportError:
# Python was built without threads
thread_module = None
setattr(self_module, '_thread', thread_module)
# Directly load the _weakref module (needed during bootstrap).
weakref_module = _bootstrap._builtin_from_name('_weakref')
setattr(self_module, '_weakref', weakref_module)
# Directly load the winreg module (needed during bootstrap).
if builtin_os == 'nt':
winreg_module = _bootstrap._builtin_from_name('winreg')
setattr(self_module, '_winreg', winreg_module)
# Constants # Constants
setattr(self_module, '_relax_case', _make_relax_case()) setattr(self_module, '_relax_case', _make_relax_case())
EXTENSION_SUFFIXES.extend(_imp.extension_suffixes()) EXTENSION_SUFFIXES.extend(_imp.extension_suffixes())
if builtin_os == 'nt':
SOURCE_SUFFIXES.append('.pyw')
if '_d.pyd' in EXTENSION_SUFFIXES:
WindowsRegistryFinder.DEBUG_BUILD = True
def _install(_bootstrap_module): def _install(_bootstrap_module):
"""Install the path-based import components.""" """Install the path-based import components."""