Added a context manager to swap architectures
This solves a few FIXMEs in conftest.py, where
we were manipulating globals and seeing side
effects prior to registering fixtures.
This commit solves the FIXMEs, but introduces
a performance regression on tests that may need
to be investigated
(cherry picked from commit 4558dc06e2
)
This commit is contained in:
parent
095ace9028
commit
2a5f46d8d3
5 changed files with 89 additions and 35 deletions
|
@ -56,6 +56,7 @@
|
||||||
attributes front_os and back_os. The operating system as described earlier,
|
attributes front_os and back_os. The operating system as described earlier,
|
||||||
will be responsible for compiler detection.
|
will be responsible for compiler detection.
|
||||||
"""
|
"""
|
||||||
|
import contextlib
|
||||||
import functools
|
import functools
|
||||||
import inspect
|
import inspect
|
||||||
import warnings
|
import warnings
|
||||||
|
@ -67,6 +68,8 @@
|
||||||
from llnl.util.lang import memoized, list_modules, key_ordering
|
from llnl.util.lang import memoized, list_modules, key_ordering
|
||||||
|
|
||||||
import spack.compiler
|
import spack.compiler
|
||||||
|
import spack.compilers
|
||||||
|
import spack.config
|
||||||
import spack.paths
|
import spack.paths
|
||||||
import spack.error as serr
|
import spack.error as serr
|
||||||
import spack.util.executable
|
import spack.util.executable
|
||||||
|
@ -491,7 +494,7 @@ def arch_for_spec(arch_spec):
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def all_platforms():
|
def _all_platforms():
|
||||||
classes = []
|
classes = []
|
||||||
mod_path = spack.paths.platform_path
|
mod_path = spack.paths.platform_path
|
||||||
parent_module = "spack.platforms"
|
parent_module = "spack.platforms"
|
||||||
|
@ -512,7 +515,7 @@ def all_platforms():
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def platform():
|
def _platform():
|
||||||
"""Detects the platform for this machine.
|
"""Detects the platform for this machine.
|
||||||
|
|
||||||
Gather a list of all available subclasses of platforms.
|
Gather a list of all available subclasses of platforms.
|
||||||
|
@ -521,7 +524,7 @@ def platform():
|
||||||
a file path (/opt/cray...)
|
a file path (/opt/cray...)
|
||||||
"""
|
"""
|
||||||
# Try to create a Platform object using the config file FIRST
|
# Try to create a Platform object using the config file FIRST
|
||||||
platform_list = all_platforms()
|
platform_list = _all_platforms()
|
||||||
platform_list.sort(key=lambda a: a.priority)
|
platform_list.sort(key=lambda a: a.priority)
|
||||||
|
|
||||||
for platform_cls in platform_list:
|
for platform_cls in platform_list:
|
||||||
|
@ -529,6 +532,19 @@ def platform():
|
||||||
return platform_cls()
|
return platform_cls()
|
||||||
|
|
||||||
|
|
||||||
|
#: The "real" platform of the host running Spack. This should not be changed
|
||||||
|
#: by any method and is here as a convenient way to refer to the host platform.
|
||||||
|
real_platform = _platform
|
||||||
|
|
||||||
|
#: The current platform used by Spack. May be swapped by the use_platform
|
||||||
|
#: context manager.
|
||||||
|
platform = _platform
|
||||||
|
|
||||||
|
#: The list of all platform classes. May be swapped by the use_platform
|
||||||
|
#: context manager.
|
||||||
|
all_platforms = _all_platforms
|
||||||
|
|
||||||
|
|
||||||
@memoized
|
@memoized
|
||||||
def default_arch():
|
def default_arch():
|
||||||
"""Default ``Arch`` object for this machine.
|
"""Default ``Arch`` object for this machine.
|
||||||
|
@ -563,3 +579,39 @@ def compatible_sys_types():
|
||||||
arch = Arch(platform(), 'default_os', target)
|
arch = Arch(platform(), 'default_os', target)
|
||||||
compatible_archs.append(str(arch))
|
compatible_archs.append(str(arch))
|
||||||
return compatible_archs
|
return compatible_archs
|
||||||
|
|
||||||
|
|
||||||
|
class _PickleableCallable(object):
|
||||||
|
"""Class used to pickle a callable that may substitute either
|
||||||
|
_platform or _all_platforms. Lambda or nested functions are
|
||||||
|
not pickleable.
|
||||||
|
"""
|
||||||
|
def __init__(self, return_value):
|
||||||
|
self.return_value = return_value
|
||||||
|
|
||||||
|
def __call__(self):
|
||||||
|
return self.return_value
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def use_platform(new_platform):
|
||||||
|
global platform, all_platforms
|
||||||
|
|
||||||
|
msg = '"{0}" must be an instance of Platform'
|
||||||
|
assert isinstance(new_platform, Platform), msg.format(new_platform)
|
||||||
|
|
||||||
|
original_platform_fn, original_all_platforms_fn = platform, all_platforms
|
||||||
|
platform = _PickleableCallable(new_platform)
|
||||||
|
all_platforms = _PickleableCallable([type(new_platform)])
|
||||||
|
|
||||||
|
# Clear configuration and compiler caches
|
||||||
|
spack.config.config.clear_caches()
|
||||||
|
spack.compilers._cache_config_files = []
|
||||||
|
|
||||||
|
yield new_platform
|
||||||
|
|
||||||
|
platform, all_platforms = original_platform_fn, original_all_platforms_fn
|
||||||
|
|
||||||
|
# Clear configuration and compiler caches
|
||||||
|
spack.config.config.clear_caches()
|
||||||
|
spack.compilers._cache_config_files = []
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
""" Test checks if the architecture class is created correctly and also that
|
""" Test checks if the architecture class is created correctly and also that
|
||||||
the functions are looking for the correct architecture name
|
the functions are looking for the correct architecture name
|
||||||
"""
|
"""
|
||||||
|
import itertools
|
||||||
import os
|
import os
|
||||||
import platform as py_platform
|
import platform as py_platform
|
||||||
|
|
||||||
|
@ -116,20 +117,26 @@ def test_user_defaults(config):
|
||||||
assert default_target == default_spec.architecture.target
|
assert default_target == default_spec.architecture.target
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('operating_system', [
|
def test_user_input_combination(config):
|
||||||
x for x in spack.architecture.platform().operating_sys
|
valid_keywords = ["fe", "be", "frontend", "backend"]
|
||||||
] + ["fe", "be", "frontend", "backend"])
|
|
||||||
@pytest.mark.parametrize('target', [
|
possible_targets = ([x for x in spack.architecture.platform().targets]
|
||||||
x for x in spack.architecture.platform().targets
|
+ valid_keywords)
|
||||||
] + ["fe", "be", "frontend", "backend"])
|
|
||||||
def test_user_input_combination(config, operating_system, target):
|
possible_os = ([x for x in spack.architecture.platform().operating_sys]
|
||||||
platform = spack.architecture.platform()
|
+ valid_keywords)
|
||||||
spec = Spec("libelf os=%s target=%s" % (operating_system, target))
|
|
||||||
spec.concretize()
|
for target, operating_system in itertools.product(
|
||||||
assert spec.architecture.os == str(
|
possible_targets, possible_os
|
||||||
platform.operating_system(operating_system)
|
):
|
||||||
)
|
platform = spack.architecture.platform()
|
||||||
assert spec.architecture.target == platform.target(target)
|
spec_str = "libelf os={0} target={1}".format(operating_system, target)
|
||||||
|
spec = Spec(spec_str)
|
||||||
|
spec.concretize()
|
||||||
|
assert spec.architecture.os == str(
|
||||||
|
platform.operating_system(operating_system)
|
||||||
|
)
|
||||||
|
assert spec.architecture.target == platform.target(target)
|
||||||
|
|
||||||
|
|
||||||
def test_operating_system_conversion_to_dict():
|
def test_operating_system_conversion_to_dict():
|
||||||
|
|
|
@ -27,7 +27,7 @@ def python_database(mock_packages, mutable_database):
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.db
|
@pytest.mark.db
|
||||||
def test_extensions(mock_packages, python_database, capsys):
|
def test_extensions(mock_packages, python_database, config, capsys):
|
||||||
ext2 = Spec("py-extension2").concretized()
|
ext2 = Spec("py-extension2").concretized()
|
||||||
|
|
||||||
def check_output(ni, na):
|
def check_output(ni, na):
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
concretize = SpackCommand('concretize')
|
concretize = SpackCommand('concretize')
|
||||||
|
|
||||||
|
|
||||||
def test_undevelop(tmpdir, mock_packages, mutable_mock_env_path):
|
def test_undevelop(tmpdir, config, mock_packages, mutable_mock_env_path):
|
||||||
# setup environment
|
# setup environment
|
||||||
envdir = tmpdir.mkdir('env')
|
envdir = tmpdir.mkdir('env')
|
||||||
with envdir.as_cwd():
|
with envdir.as_cwd():
|
||||||
|
@ -39,7 +39,7 @@ def test_undevelop(tmpdir, mock_packages, mutable_mock_env_path):
|
||||||
assert not after.satisfies('dev_path=*')
|
assert not after.satisfies('dev_path=*')
|
||||||
|
|
||||||
|
|
||||||
def test_undevelop_nonexistent(tmpdir, mock_packages, mutable_mock_env_path):
|
def test_undevelop_nonexistent(tmpdir, config, mock_packages, mutable_mock_env_path):
|
||||||
# setup environment
|
# setup environment
|
||||||
envdir = tmpdir.mkdir('env')
|
envdir = tmpdir.mkdir('env')
|
||||||
with envdir.as_cwd():
|
with envdir.as_cwd():
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Copyright 2013-2020 Lawrence Livermore National Security, LLC and other
|
# Copyright 2013-2021 Lawrence Livermore National Security, LLC and other
|
||||||
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
@ -315,24 +315,18 @@ def _skip_if_missing_executables(request):
|
||||||
pytest.skip(msg.format(', '.join(missing_execs)))
|
pytest.skip(msg.format(', '.join(missing_execs)))
|
||||||
|
|
||||||
|
|
||||||
# FIXME: The lines below should better be added to a fixture with
|
@pytest.fixture(scope='session')
|
||||||
# FIXME: session-scope. Anyhow doing it is not easy, as it seems
|
|
||||||
# FIXME: there's some weird interaction with compilers during concretization.
|
|
||||||
spack.architecture.real_platform = spack.architecture.platform
|
|
||||||
|
|
||||||
|
|
||||||
def test_platform():
|
def test_platform():
|
||||||
return spack.platforms.test.Test()
|
return spack.platforms.test.Test()
|
||||||
|
|
||||||
|
|
||||||
spack.architecture.platform = test_platform
|
@pytest.fixture(autouse=True, scope='session')
|
||||||
|
def _use_test_platform(test_platform):
|
||||||
|
# This is the only context manager used at session scope (see note
|
||||||
# FIXME: Since we change the architecture above, we have to (re)initialize
|
# below for more insight) since we want to use the test platform as
|
||||||
# FIXME: the config singleton. If it gets initialized too early with the
|
# a default during tests.
|
||||||
# FIXME: actual architecture, tests will fail.
|
with spack.architecture.use_platform(test_platform):
|
||||||
spack.config.config = spack.config._config()
|
yield
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Note on context managers used by fixtures
|
# Note on context managers used by fixtures
|
||||||
|
@ -356,6 +350,7 @@ def test_platform():
|
||||||
# *USE*, or things can get really confusing.
|
# *USE*, or things can get really confusing.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Test-specific fixtures
|
# Test-specific fixtures
|
||||||
#
|
#
|
||||||
|
|
Loading…
Reference in a new issue