From 01a444c023fbc109d0ad1a563c80e3a98c067cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Cepl?= Date: Wed, 25 Jul 2018 12:25:03 +0200 Subject: [PATCH] Replace use of imp library with importlib. imp library has been deprecated since 3.4 and in 3.7 it finally breaks builds. Presrving compatibility with python >= 2.7. --- src/CHANGES.txt | 3 + src/engine/SCons/Platform/__init__.py | 13 +-- src/engine/SCons/Script/Main.py | 95 ++++++++++++++------- src/engine/SCons/Tool/__init__.py | 5 +- src/engine/SCons/Tool/packaging/__init__.py | 29 +++++-- src/engine/SCons/Util.py | 2 + src/engine/SCons/compat/__init__.py | 8 +- 7 files changed, 102 insertions(+), 53 deletions(-) --- a/engine/SCons/Platform/__init__.py +++ b/engine/SCons/Platform/__init__.py @@ -47,14 +47,15 @@ __revision__ = "src/engine/SCons/Platfor import SCons.compat -import imp import os +import importlib import sys import tempfile import SCons.Errors import SCons.Subst import SCons.Tool +import SCons.Util def platform_default(): @@ -100,13 +101,7 @@ def platform_module(name = platform_defa eval(full_name) else: try: - file, path, desc = imp.find_module(name, - sys.modules['SCons.Platform'].__path__) - try: - mod = imp.load_module(full_name, file, path, desc) - finally: - if file: - file.close() + mod = importlib.import_module(full_name) except ImportError: try: import zipimport @@ -114,7 +109,7 @@ def platform_module(name = platform_defa mod = importer.load_module(full_name) except ImportError: raise SCons.Errors.UserError("No platform named '%s'" % name) - setattr(SCons.Platform, name, mod) + setattr(SCons.Platform, name, mod) return sys.modules[full_name] def DefaultToolList(platform, env): --- a/engine/SCons/Script/Main.py +++ b/engine/SCons/Script/Main.py @@ -711,54 +711,89 @@ def _load_site_scons_dir(topdir, site_di sys.path = [os.path.abspath(site_dir)] + sys.path site_init_file = os.path.join(site_dir, site_init_filename) site_tools_dir = os.path.join(site_dir, site_tools_dirname) - if os.path.exists(site_init_file): - import imp, re - try: + + if SCons.Util.PY2 or SCons.Util.PY34: + if os.path.exists(site_init_file): + import imp, re try: - fp, pathname, description = imp.find_module(site_init_modname, - [site_dir]) - # Load the file into SCons.Script namespace. This is - # opaque and clever; m is the module object for the - # SCons.Script module, and the exec ... in call executes a - # file (or string containing code) in the context of the - # module's dictionary, so anything that code defines ends - # up adding to that module. This is really short, but all - # the error checking makes it longer. + try: + fp, pathname, description = imp.find_module(site_init_modname, + [site_dir]) + # Load the file into SCons.Script namespace. This is + # opaque and clever; m is the module object for the + # SCons.Script module, and the exec ... in call executes a + # file (or string containing code) in the context of the + # module's dictionary, so anything that code defines ends + # up adding to that module. This is really short, but all + # the error checking makes it longer. + try: + m = sys.modules['SCons.Script'] + except Exception as e: + fmt = 'cannot import site_init.py: missing SCons.Script module {}' + raise SCons.Errors.InternalError(fmt.format(repr(e))) + try: + sfx = description[0] + modname = os.path.basename(pathname)[:-len(sfx)] + site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} + re_special = re.compile("__[^_]+__") + for k in list(m.__dict__.keys()): + if not re_special.match(k): + site_m[k] = m.__dict__[k] + + # This is the magic. + exec(compile(fp.read(), fp.name, 'exec'), site_m) + except KeyboardInterrupt: + raise + except Exception as e: + fmt = '*** Error loading site_init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + else: + for k in site_m: + if not re_special.match(k): + m.__dict__[k] = site_m[k] + except KeyboardInterrupt: + raise + except ImportError as e: + fmt = '*** cannot import site init file %s:\n' + sys.stderr.write(fmt % repr(site_init_file)) + raise + finally: + if fp: + fp.close() + else: # Python >= 3.5 + if os.path.exists(site_init_file): + import importlib.util, importlib.machinery + try: + loader_details = ( + importlib.machinery.ExtensionFileLoader, + importlib.machinery.EXTENSION_SUFFIXES + ) + finder = importlib.machinery.FileFinder(site_dir, + loader_details) + spec = finder.find_spec(site_init_modname) try: m = sys.modules['SCons.Script'] except Exception as e: fmt = 'cannot import site_init.py: missing SCons.Script module %s' raise SCons.Errors.InternalError(fmt % repr(e)) try: - sfx = description[0] - modname = os.path.basename(pathname)[:-len(sfx)] - site_m = {"__file__": pathname, "__name__": modname, "__doc__": None} - re_special = re.compile("__[^_]+__") - for k in list(m.__dict__.keys()): - if not re_special.match(k): - site_m[k] = m.__dict__[k] - + mod = importlib.util.module_from_spec(spec) # This is the magic. - exec(compile(fp.read(), fp.name, 'exec'), site_m) + exec(mod.__loader__.get_code(mod.__name__), m.__dict__) except KeyboardInterrupt: raise except Exception as e: - fmt = '*** Error loading site_init file %s:\n' - sys.stderr.write(fmt % repr(site_init_file)) + fmt = '*** Error loading site_init file {}:\n' + sys.stderr.write(fmt.format(repr(site_init_file))) raise - else: - for k in site_m: - if not re_special.match(k): - m.__dict__[k] = site_m[k] except KeyboardInterrupt: raise except ImportError as e: fmt = '*** cannot import site init file %s:\n' sys.stderr.write(fmt % repr(site_init_file)) raise - finally: - if fp: - fp.close() + if os.path.exists(site_tools_dir): # prepend to DefaultToolpath SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir)) --- a/engine/SCons/Tool/__init__.py +++ b/engine/SCons/Tool/__init__.py @@ -54,6 +54,7 @@ import SCons.Scanner.D import SCons.Scanner.LaTeX import SCons.Scanner.Prog import SCons.Scanner.SWIG +import SCons.Util import collections DefaultToolpath=[] @@ -136,7 +137,7 @@ class Tool(object): sys.path = self.toolpath + sys.path # sys.stderr.write("Tool:%s\nPATH:%s\n"%(self.name,sys.path)) - if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] in (0,1,2,3,4)): + if SCons.Util.PY2 or SCons.Util.PY34: # Py 2 code try: try: @@ -238,7 +239,7 @@ class Tool(object): setattr(SCons.Tool, self.name, module) found_module = module - + if found_module is not None: sys.path = oldpythonpath return found_module --- a/engine/SCons/Tool/packaging/__init__.py +++ b/engine/SCons/Tool/packaging/__init__.py @@ -30,10 +30,10 @@ __revision__ = "src/engine/SCons/Tool/pa import SCons.Environment from SCons.Variables import * from SCons.Errors import * -from SCons.Util import is_List, make_path_relative +from SCons.Util import is_List, is_String, make_path_relative, PY2, PY34 from SCons.Warnings import warn, Warning -import os, imp +import os import SCons.Defaults __all__ = [ 'src_targz', 'src_tarbz2', 'src_zip', 'tarbz2', 'targz', 'zip', 'rpm', 'msi', 'ipk' ] @@ -63,7 +63,7 @@ def Tag(env, target, source, *more_tags, for x in more_tags: kw_tags[x] = '' - if not SCons.Util.is_List(target): + if not is_List(target): target=[target] else: # hmm, sometimes the target list, is a list of a list @@ -117,8 +117,21 @@ def Package(env, target=None, source=Non # load the needed packagers. def load_packager(type): try: - file,path,desc=imp.find_module(type, __path__) - return imp.load_module(type, file, path, desc) + if PY2 or PY34: + import imp + file,path,desc=imp.find_module(type, __path__) + return imp.load_module(type, file, path, desc) + else: + import importlib.util, importlib.machinery + loader_details = ( + importlib.machinery.ExtensionFileLoader, + importlib.machinery.EXTENSION_SUFFIXES + ) + finder = importlib.machinery.FileFinder(__path__, + loader_details) + spec = finder.find_spec(type) + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) except ImportError as e: raise EnvironmentError("packager %s not available: %s"%(type,str(e))) @@ -251,12 +264,12 @@ def putintopackageroot(target, source, e All attributes of the source file will be copied to the new file. """ # make sure the packageroot is a Dir object. - if SCons.Util.is_String(pkgroot): pkgroot=env.Dir(pkgroot) - if not SCons.Util.is_List(source): source=[source] + if is_String(pkgroot): pkgroot=env.Dir(pkgroot) + if not is_List(source): source=[source] new_source = [] for file in source: - if SCons.Util.is_String(file): file = env.File(file) + if is_String(file): file = env.File(file) if file.is_under(pkgroot): new_source.append(file) --- a/engine/SCons/Util.py +++ b/engine/SCons/Util.py @@ -35,6 +35,8 @@ import codecs import pprint PY3 = sys.version_info[0] == 3 +PY2 = sys.version_info[0] == 2 +PY34 = sys.version_info[0] == 3 and sys.version_info[1] <= 4 try: from UserDict import UserDict --- a/engine/SCons/compat/__init__.py +++ b/engine/SCons/compat/__init__.py @@ -61,7 +61,7 @@ __revision__ = "src/engine/SCons/compat/ import os import sys -import imp # Use the "imp" module to protect imports from fixers. +import importlib # Use the "importlib" module to protect imports from fixers. PYPY = hasattr(sys, 'pypy_translation_info') @@ -71,8 +71,8 @@ def import_as(module, name): Imports the specified module (from our local directory) as the specified name, returning the loaded module object. """ - dir = os.path.split(__file__)[0] - return imp.load_module(name, *imp.find_module(module, [dir])) + sys.modules[name] = importlib.import_module(module) + return sys.modules[name] def rename_module(new, old): @@ -81,7 +81,7 @@ def rename_module(new, old): Used for purely cosmetic name changes in Python 3.x. """ try: - sys.modules[new] = imp.load_module(old, *imp.find_module(old)) + sys.modules[new] = importlib.import_module(old) return True except ImportError: return False