Issue #2974 - Part 51: Replace imp with importlib.

imp was actually still available up until Python 3.9 or so. Ripping
it out made me go through the headache of debugging our import logic.
Granted, it was deprecated long ago and there was plenty of warning, but
since we are doing the migration now it's all hitting at once if we want
to support any semi-modern Python.
This commit is contained in:
Jeremy Andrews committed 2026-03-20 13:29:08 -05:00
1 parent 19f0115559
commit 9c10d36094
4 files changed
+137 -32

No files matched your search

+16 -6
View File
@@ -22,13 +22,23 @@ def ancestors(path):
break break
def load_mach(dir_path, mach_path): def load_mach(dir_path, mach_path):
import imp import types
with open(mach_path, 'r') as fh: import importlib.util
imp.load_module('mach_bootstrap', fh, mach_path, import importlib.machinery
('.py', 'r', imp.PY_SOURCE)) module_name = "mach_bootstrap"
import mach_bootstrap loader = importlib.machinery.SourceFileLoader(module_name, mach_path)
return mach_bootstrap.bootstrap(dir_path) mod = types.ModuleType(module_name)
mod.__loader__ = loader
mod.__file__ = mach_path
mod.__package__ = module_name.rpartition('.')[0]
mod.__spec__ = None
sys.modules[module_name] = mod
code = loader.get_code(module_name)
exec(code, mod.__dict__)
return mod.bootstrap(dir_path)
def check_and_get_mach(dir_path): def check_and_get_mach(dir_path):
bootstrap_paths = ( bootstrap_paths = (
+18 -3
View File
@@ -10,11 +10,13 @@ from collections import Iterable
import argparse import argparse
import codecs import codecs
import imp import importlib.util
import importlib.machinery
import logging import logging
import os import os
import sys import sys
import traceback import traceback
import types
import uuid import uuid
from .base import ( from .base import (
@@ -102,6 +104,19 @@ to implement a mach command. Please fix the error, or uninstall the module from
your system. your system.
'''.lstrip() '''.lstrip()
def load_source(module_name, path):
loader = importlib.machinery.SourceFileLoader(module_name, path)
module = types.ModuleType(module_name)
module.__loader__ = loader
module.__file__ = path
module.__package__ = module_name.rpartition('.')[0]
sys.modules[module_name] = module
code = loader.get_code(module_name)
exec(code, module.__dict__)
return module
class ArgumentParser(argparse.ArgumentParser): class ArgumentParser(argparse.ArgumentParser):
"""Custom implementation argument parser to make things look pretty.""" """Custom implementation argument parser to make things look pretty."""
@@ -257,12 +272,12 @@ To see more help for a specific command, run:
# Ensure parent module is present otherwise we'll (likely) get # Ensure parent module is present otherwise we'll (likely) get
# an error due to unknown parent. # an error due to unknown parent.
if 'mach.commands' not in sys.modules: if 'mach.commands' not in sys.modules:
mod = imp.new_module('mach.commands') mod = types.ModuleType('mach.commands')
sys.modules['mach.commands'] = mod sys.modules['mach.commands'] = mod
module_name = 'mach.commands.%s' % uuid.uuid1().hex module_name = 'mach.commands.%s' % uuid.uuid1().hex
imp.load_source(module_name, path) load_source(module_name, path)
def load_commands_from_entry_point(self, group='mach.providers'): def load_commands_from_entry_point(self, group='mach.providers'):
"""Scan installed packages for mach command provider entry points. An """Scan installed packages for mach command provider entry points. An
@@ -9,7 +9,9 @@
from __future__ import absolute_import, print_function from __future__ import absolute_import, print_function
import argparse import argparse
import imp import importlib.util
import importlib.machinery
import types
import os import os
import sys import sys
import traceback import traceback
@@ -48,9 +50,19 @@ def main(argv):
# Since we're invoking the script in a roundabout way, we provide this # Since we're invoking the script in a roundabout way, we provide this
# bit of convenience. # bit of convenience.
sys.path.append(os.path.dirname(script)) sys.path.append(os.path.dirname(script))
with open(script, 'r') as fh: module_name = "script"
module = imp.load_module('script', fh, script, loader = importlib.machinery.SourceFileLoader(module_name, script)
('.py', 'r', imp.PY_SOURCE)) module = types.ModuleType(module_name)
module.__loader__ = loader
module.__file__ = script
module.__package__ = module_name.rpartition('.')[0]
module.__spec__ = None
sys.modules[module_name] = module
code = loader.get_code(module_name)
exec(code, module.__dict__)
method = args.method_name method = args.method_name
if not hasattr(module, method): if not hasattr(module, method):
print('Error: script "{0}" is missing a {1} method'.format(script, method), print('Error: script "{0}" is missing a {1} method'.format(script, method),
+87 -19
View File
@@ -1050,38 +1050,106 @@ def change_prefix(filename, dst_prefix):
assert False, "Filename %s does not start with any of these prefixes: %s" % \ assert False, "Filename %s does not start with any of these prefixes: %s" % \
(filename, prefixes) (filename, prefixes)
def find_module_info(modname):
for finder in sys.meta_path:
try:
loader = finder.find_module(modname)
except Exception:
continue
if loader is None:
continue
# get filename
try:
filename = loader.get_filename(modname)
except Exception:
continue
# detect package
is_pkg = False
try:
is_pkg = loader.is_package(modname)
except Exception:
pass
return filename, is_pkg
raise ImportError("Cannot find module %s" % modname)
def copy_required_modules(dst_prefix, symlink): def copy_required_modules(dst_prefix, symlink):
import imp import importlib
import importlib.machinery
from shutil import copytree
for modname in REQUIRED_MODULES: for modname in REQUIRED_MODULES:
if modname in sys.builtin_module_names: if modname in sys.builtin_module_names:
logger.info("Ignoring built-in bootstrap module: %s" % modname) logger.info("Ignoring built-in bootstrap module: %s" % modname)
continue continue
try:
f, filename, _ = imp.find_module(modname) filename = None
except ImportError: is_pkg = False
for finder in sys.meta_path:
try:
loader = finder.find_module(modname)
except Exception:
continue
if loader is None:
continue
# get filename (__init__.py for packages)
try:
filename = loader.get_filename(modname)
except Exception:
continue
# detect package
try:
is_pkg = loader.is_package(modname)
except Exception:
is_pkg = False
break
if filename is None:
logger.info("Cannot import bootstrap module: %s" % modname) logger.info("Cannot import bootstrap module: %s" % modname)
else: continue
if f is not None:
f.close() # special-case custom readline.so on OS X, but not for pypy:
# special-case custom readline.so on OS X, but not for pypy: if (modname == 'readline' and sys.platform == 'darwin' and
if modname == 'readline' and sys.platform == 'darwin' and not ( not (is_pypy or filename.endswith(join('lib-dynload', 'readline.so')))):
is_pypy or filename.endswith(join('lib-dynload', 'readline.so'))): dst_filename = join(dst_prefix, 'lib',
dst_filename = join(dst_prefix, 'lib', 'python%s' % sys.version[:3], 'readline.so') 'python%s' % sys.version[:3],
elif modname == 'readline' and sys.platform == 'win32': 'readline.so')
# special-case for Windows, where readline is not a
# standard module, though it may have been installed in # copy single file
# site-packages by a third-party package
pass
else:
dst_filename = change_prefix(filename, dst_prefix)
copyfile(filename, dst_filename, symlink) copyfile(filename, dst_filename, symlink)
continue
if modname == 'readline' and sys.platform == 'win32':
# special-case for Windows, where readline is not a
# standard module, though it may have been installed in
# site-packages by a third-party package
continue
if is_pkg:
# copy entire package directory
pkg_dir = os.path.dirname(filename)
dst_dir = change_prefix(pkg_dir, dst_prefix)
copytree(pkg_dir, dst_dir, symlink)
else:
# copy single module file
dst_filename = change_prefix(filename, dst_prefix)
copyfile(filename, dst_filename, symlink)
# also copy .py if we got a .pyc
if filename.endswith('.pyc'): if filename.endswith('.pyc'):
pyfile = filename[:-1] pyfile = filename[:-1]
if os.path.exists(pyfile): if os.path.exists(pyfile):
copyfile(pyfile, dst_filename[:-1], symlink) copyfile(pyfile, dst_filename[:-1], symlink)
def subst_path(prefix_path, prefix, home_dir): def subst_path(prefix_path, prefix, home_dir):
prefix_path = os.path.normpath(prefix_path) prefix_path = os.path.normpath(prefix_path)
prefix = os.path.normpath(prefix) prefix = os.path.normpath(prefix)