imports: spack uses importlib instead of imp when available
- `imp` is deprecated and seems to have started having some weird issues on certain Linux versions. - In particular, the file argument to `load_source` is ignored on arch linux with Python 3.7. - `imp` is the only way to do imports in 2.6, so we'll keep it around for now and use it if importlib won't work. - `importlib` is the new import system, and it allows us to get lower-level access to the import implementation. - This consolidates all import logic into `spack.util.imp`, and make it use `importlib` if it's avialable.
This commit is contained in:
parent
f838b5e8c0
commit
39c9bbfbbb
7 changed files with 199 additions and 39 deletions
|
@ -26,7 +26,6 @@
|
||||||
system and configuring Spack to use multiple compilers.
|
system and configuring Spack to use multiple compilers.
|
||||||
"""
|
"""
|
||||||
import os
|
import os
|
||||||
import imp
|
|
||||||
|
|
||||||
from llnl.util.lang import list_modules
|
from llnl.util.lang import list_modules
|
||||||
|
|
||||||
|
@ -35,7 +34,7 @@
|
||||||
import spack.spec
|
import spack.spec
|
||||||
import spack.config
|
import spack.config
|
||||||
import spack.architecture
|
import spack.architecture
|
||||||
|
import spack.util.imp as simp
|
||||||
from spack.util.naming import mod_to_class
|
from spack.util.naming import mod_to_class
|
||||||
|
|
||||||
_imported_compilers_module = 'spack.compilers'
|
_imported_compilers_module = 'spack.compilers'
|
||||||
|
@ -361,7 +360,7 @@ def class_for_compiler_name(compiler_name):
|
||||||
assert(supported(compiler_name))
|
assert(supported(compiler_name))
|
||||||
|
|
||||||
file_path = os.path.join(spack.paths.compilers_path, compiler_name + ".py")
|
file_path = os.path.join(spack.paths.compilers_path, compiler_name + ".py")
|
||||||
compiler_mod = imp.load_source(_imported_compilers_module, file_path)
|
compiler_mod = simp.load_source(_imported_compilers_module, file_path)
|
||||||
cls = getattr(compiler_mod, mod_to_class(compiler_name))
|
cls = getattr(compiler_mod, mod_to_class(compiler_name))
|
||||||
|
|
||||||
# make a note of the name in the module so we can get to it easily.
|
# make a note of the name in the module so we can get to it easily.
|
||||||
|
|
|
@ -48,7 +48,6 @@ class OpenMpi(Package):
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
@ -115,11 +114,11 @@ def __new__(cls, name, bases, attr_dict):
|
||||||
def __init__(cls, name, bases, attr_dict):
|
def __init__(cls, name, bases, attr_dict):
|
||||||
# The class is being created: if it is a package we must ensure
|
# The class is being created: if it is a package we must ensure
|
||||||
# that the directives are called on the class to set it up
|
# that the directives are called on the class to set it up
|
||||||
module = inspect.getmodule(cls)
|
|
||||||
if 'spack.pkg' in module.__name__:
|
if 'spack.pkg' in cls.__module__:
|
||||||
# Package name as taken
|
# Package name as taken
|
||||||
# from llnl.util.lang.get_calling_module_name
|
# from llnl.util.lang.get_calling_module_name
|
||||||
pkg_name = module.__name__.split('.')[-1]
|
pkg_name = cls.__module__.split('.')[-1]
|
||||||
setattr(cls, 'name', pkg_name)
|
setattr(cls, 'name', pkg_name)
|
||||||
|
|
||||||
# Ensure the presence of the dictionaries associated
|
# Ensure the presence of the dictionaries associated
|
||||||
|
|
|
@ -41,10 +41,10 @@
|
||||||
systems (e.g. modules, dotkit, etc.) or to add other custom
|
systems (e.g. modules, dotkit, etc.) or to add other custom
|
||||||
features.
|
features.
|
||||||
"""
|
"""
|
||||||
import imp
|
|
||||||
import os.path
|
import os.path
|
||||||
|
|
||||||
import spack.paths
|
import spack.paths
|
||||||
|
import spack.util.imp as simp
|
||||||
from llnl.util.lang import memoized, list_modules
|
from llnl.util.lang import memoized, list_modules
|
||||||
|
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ def all_hook_modules():
|
||||||
for name in list_modules(spack.paths.hooks_path):
|
for name in list_modules(spack.paths.hooks_path):
|
||||||
mod_name = __name__ + '.' + name
|
mod_name = __name__ + '.' + name
|
||||||
path = os.path.join(spack.paths.hooks_path, name) + ".py"
|
path = os.path.join(spack.paths.hooks_path, name) + ".py"
|
||||||
mod = imp.load_source(mod_name, path)
|
mod = simp.load_source(mod_name, path)
|
||||||
modules.append(mod)
|
modules.append(mod)
|
||||||
|
|
||||||
return modules
|
return modules
|
||||||
|
|
|
@ -29,10 +29,8 @@
|
||||||
import errno
|
import errno
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
import inspect
|
||||||
import imp
|
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
import tempfile
|
|
||||||
import json
|
import json
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from six import string_types
|
from six import string_types
|
||||||
|
@ -54,11 +52,13 @@
|
||||||
import spack.caches
|
import spack.caches
|
||||||
import spack.error
|
import spack.error
|
||||||
import spack.spec
|
import spack.spec
|
||||||
|
import spack.util.imp as simp
|
||||||
from spack.provider_index import ProviderIndex
|
from spack.provider_index import ProviderIndex
|
||||||
from spack.util.path import canonicalize_path
|
from spack.util.path import canonicalize_path
|
||||||
from spack.util.naming import NamespaceTrie, valid_module_name
|
from spack.util.naming import NamespaceTrie, valid_module_name
|
||||||
from spack.util.naming import mod_to_class, possible_spack_module_names
|
from spack.util.naming import mod_to_class, possible_spack_module_names
|
||||||
|
|
||||||
|
|
||||||
#: Super-namespace for all packages.
|
#: Super-namespace for all packages.
|
||||||
#: Package modules are imported as spack.pkg.<namespace>.<pkg-name>.
|
#: Package modules are imported as spack.pkg.<namespace>.<pkg-name>.
|
||||||
repo_namespace = 'spack.pkg'
|
repo_namespace = 'spack.pkg'
|
||||||
|
@ -994,9 +994,8 @@ def _get_pkg_module(self, pkg_name):
|
||||||
fullname = "%s.%s" % (self.full_namespace, pkg_name)
|
fullname = "%s.%s" % (self.full_namespace, pkg_name)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with import_lock():
|
module = simp.load_source(fullname, file_path,
|
||||||
with prepend_open(file_path, text=_package_prepend) as f:
|
prepend=_package_prepend)
|
||||||
module = imp.load_source(fullname, file_path, f)
|
|
||||||
except SyntaxError as e:
|
except SyntaxError as e:
|
||||||
# SyntaxError strips the path from the filename so we need to
|
# SyntaxError strips the path from the filename so we need to
|
||||||
# manually construct the error message in order to give the
|
# manually construct the error message in order to give the
|
||||||
|
@ -1146,31 +1145,6 @@ def set_path(repo):
|
||||||
return append
|
return append
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def import_lock():
|
|
||||||
imp.acquire_lock()
|
|
||||||
yield
|
|
||||||
imp.release_lock()
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
|
||||||
def prepend_open(f, *args, **kwargs):
|
|
||||||
"""Open a file for reading, but prepend with some text prepended
|
|
||||||
|
|
||||||
Arguments are same as for ``open()``, with one keyword argument,
|
|
||||||
``text``, specifying the text to prepend.
|
|
||||||
"""
|
|
||||||
text = kwargs.get('text', None)
|
|
||||||
|
|
||||||
with open(f, *args) as f:
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w+') as tf:
|
|
||||||
if text:
|
|
||||||
tf.write(text + '\n')
|
|
||||||
tf.write(f.read())
|
|
||||||
tf.seek(0)
|
|
||||||
yield tf.file
|
|
||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def swap(repo_path):
|
def swap(repo_path):
|
||||||
"""Temporarily use another RepoPath."""
|
"""Temporarily use another RepoPath."""
|
||||||
|
|
41
lib/spack/spack/util/imp/__init__.py
Normal file
41
lib/spack/spack/util/imp/__init__.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/spack/spack
|
||||||
|
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License (as
|
||||||
|
# published by the Free Software Foundation) version 2.1, February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
"""Consolidated module for all imports done by Spack.
|
||||||
|
|
||||||
|
Many parts of Spack have to import Python code. This utility package
|
||||||
|
wraps Spack's interface with Python's import system.
|
||||||
|
|
||||||
|
We do this because Python's import system is confusing and changes from
|
||||||
|
Python version to Python version, and we should be able to adapt our
|
||||||
|
approach to the underlying implementation.
|
||||||
|
|
||||||
|
Currently, this uses ``importlib.machinery`` where available and ``imp``
|
||||||
|
when ``importlib`` is not completely usable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from .importlib_importer import load_source # noqa
|
||||||
|
except ImportError:
|
||||||
|
from .imp_importer import load_source # noqa
|
86
lib/spack/spack/util/imp/imp_importer.py
Normal file
86
lib/spack/spack/util/imp/imp_importer.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/spack/spack
|
||||||
|
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License (as
|
||||||
|
# published by the Free Software Foundation) version 2.1, February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
"""Implementation of Spack imports that uses imp underneath.
|
||||||
|
|
||||||
|
``imp`` is deprecated in newer versions of Python, but is the only option
|
||||||
|
in Python 2.6.
|
||||||
|
"""
|
||||||
|
import imp
|
||||||
|
import tempfile
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def import_lock():
|
||||||
|
imp.acquire_lock()
|
||||||
|
yield
|
||||||
|
imp.release_lock()
|
||||||
|
|
||||||
|
|
||||||
|
def load_source(full_name, path, prepend=None):
|
||||||
|
"""Import a Python module from source.
|
||||||
|
|
||||||
|
Load the source file and add it to ``sys.modules``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
full_name (str): full name of the module to be loaded
|
||||||
|
path (str): path to the file that should be loaded
|
||||||
|
prepend (str, optional): some optional code to prepend to the
|
||||||
|
loaded module; e.g., can be used to inject import statements
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(ModuleType): the loaded module
|
||||||
|
"""
|
||||||
|
with import_lock():
|
||||||
|
if prepend is None:
|
||||||
|
return imp.load_source(full_name, path)
|
||||||
|
else:
|
||||||
|
with prepend_open(path, text=prepend) as f:
|
||||||
|
return imp.load_source(full_name, path, f)
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def prepend_open(f, *args, **kwargs):
|
||||||
|
"""Open a file for reading, but prepend with some text prepended
|
||||||
|
|
||||||
|
Arguments are same as for ``open()``, with one keyword argument,
|
||||||
|
``text``, specifying the text to prepend.
|
||||||
|
|
||||||
|
We have to write and read a tempfile for the ``imp``-based importer,
|
||||||
|
as the ``file`` argument to ``imp.load_source()`` requires a
|
||||||
|
low-level file handle.
|
||||||
|
|
||||||
|
See the ``importlib``-based importer for a faster way to do this in
|
||||||
|
later versions of python.
|
||||||
|
"""
|
||||||
|
text = kwargs.get('text', None)
|
||||||
|
|
||||||
|
with open(f, *args) as f:
|
||||||
|
with tempfile.NamedTemporaryFile(mode='w+') as tf:
|
||||||
|
if text:
|
||||||
|
tf.write(text + '\n')
|
||||||
|
tf.write(f.read())
|
||||||
|
tf.seek(0)
|
||||||
|
yield tf.file
|
61
lib/spack/spack/util/imp/importlib_importer.py
Normal file
61
lib/spack/spack/util/imp/importlib_importer.py
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
##############################################################################
|
||||||
|
# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
|
||||||
|
# Produced at the Lawrence Livermore National Laboratory.
|
||||||
|
#
|
||||||
|
# This file is part of Spack.
|
||||||
|
# Created by Todd Gamblin, tgamblin@llnl.gov, All rights reserved.
|
||||||
|
# LLNL-CODE-647188
|
||||||
|
#
|
||||||
|
# For details, see https://github.com/spack/spack
|
||||||
|
# Please also see the NOTICE and LICENSE files for our notice and the LGPL.
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Lesser General Public License (as
|
||||||
|
# published by the Free Software Foundation) version 2.1, February 1999.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and
|
||||||
|
# conditions of the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
##############################################################################
|
||||||
|
"""Implementation of Spack imports that uses importlib underneath.
|
||||||
|
|
||||||
|
``importlib`` is only fully implemented in Python 3.
|
||||||
|
"""
|
||||||
|
from importlib.machinery import SourceFileLoader
|
||||||
|
|
||||||
|
|
||||||
|
class PrependFileLoader(SourceFileLoader):
|
||||||
|
def __init__(self, full_name, path, prepend=None):
|
||||||
|
super(PrependFileLoader, self).__init__(full_name, path)
|
||||||
|
self.prepend = prepend
|
||||||
|
|
||||||
|
def get_data(self, path):
|
||||||
|
data = super(PrependFileLoader, self).get_data(path)
|
||||||
|
if path != self.path or self.prepend is None:
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
return self.prepend.encode() + b"\n" + data
|
||||||
|
|
||||||
|
|
||||||
|
def load_source(full_name, path, prepend=None):
|
||||||
|
"""Import a Python module from source.
|
||||||
|
|
||||||
|
Load the source file and add it to ``sys.modules``.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
full_name (str): full name of the module to be loaded
|
||||||
|
path (str): path to the file that should be loaded
|
||||||
|
prepend (str, optional): some optional code to prepend to the
|
||||||
|
loaded module; e.g., can be used to inject import statements
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(ModuleType): the loaded module
|
||||||
|
"""
|
||||||
|
# use our custom loader
|
||||||
|
loader = PrependFileLoader(full_name, path, prepend)
|
||||||
|
return loader.load_module()
|
Loading…
Reference in a new issue